module Biegunka.Script
( Script(..), Annotations, Annotate(..)
, script, annotate, rewind, URI, sourced, actioned, constructDestinationFilepath
, token, app, profiles, profileName, source, sourceURL, order
, runScript, runScript', evalScript
) where
import Control.Applicative (Applicative(..), (<$))
import Control.Monad (when)
import Data.List (isSuffixOf)
import Control.Lens hiding (Action)
import Control.Monad.Free (Free(..), iter, liftF)
import Control.Monad.State (MonadState(..), StateT(..), State, execState, lift, state)
import Data.Default (Default(..))
import Data.Copointed (copoint)
import Data.Set (Set)
import qualified Data.Set as S
import System.FilePath.Lens
import Biegunka.Language
data family Annotate (sc :: Scope) :: *
data instance Annotate Sources = AS { asToken :: Int, asProfile :: String }
data instance Annotate Actions = AA { aaURI :: URI, aaOrder :: Int, aaMaxOrder :: Int }
newtype Script s a = Script
{ unScript :: StateT Annotations (Free (Term Annotate s)) a
}
instance Functor (Script s) where
fmap f (Script m) = Script (fmap f m)
instance Applicative (Script s) where
pure v = Script (pure v)
Script m <*> Script n = Script (m <*> n)
instance Monad (Script s) where
return v = Script (return v)
Script m >>= f = Script (m >>= unScript . f)
instance Default a => Default (Script s a) where
def = return def
runScript :: Annotations -> Script s a -> Free (Term Annotate s) (a, Annotations)
runScript as (Script s) = runStateT s as
runScript' :: Annotations -> Script s a -> (Free (Term Annotate s) a, Annotations)
runScript' as (Script s) =
let ast = runStateT s as
(a, as') = iter copoint ast
in (a <$ ast, as')
evalScript :: Annotations -> Script s a -> Free (Term Annotate s) a
evalScript = (fmap fst .) . runScript
type URI = String
data Annotations = Annotations
{ _token :: Int
, _app :: FilePath
, _profiles :: Set String
, _profileName :: String
, _source :: FilePath
, _sourceURL :: URI
, _order :: Int
, _maxOrder :: Int
} deriving (Show, Read)
instance Default Annotations where
def = Annotations
{ _token = 0
, _app = ""
, _profiles = S.empty
, _profileName = ""
, _source = ""
, _sourceURL = ""
, _order = 0
, _maxOrder = 0
}
makeLensesWith ?? ''Annotations $ defaultRules & generateSignatures .~ False
token :: Lens' Annotations Int
app :: Lens' Annotations FilePath
profiles :: Lens' Annotations (Set String)
profileName :: Lens' Annotations String
source :: Lens' Annotations FilePath
sourceURL :: Lens' Annotations String
order :: Lens' Annotations Int
maxOrder :: Lens' Annotations Int
script :: Term Annotate s a -> Script s a
script = Script . lift . liftF
annotate :: Script s a -> StateT Annotations (Free (Term Annotate t)) (Free (Term Annotate s) a)
annotate i = state $ \s ->
let r = runScript s i
ast = fmap fst r
s' = iter copoint $ fmap snd r
in (ast, s')
rewind :: MonadState s m => Lens' s a -> m b -> m a
rewind l mb = do
a <- use l
mb
a' <- use l
l .= a
return a'
sourced :: String -> URI -> FilePath
-> Script Actions () -> (FilePath -> IO ()) -> Script Sources ()
sourced ty url path inner update = Script $ do
rfp <- use app
tok <- use token
let df = constructDestinationFilepath rfp url path
source .= df
sourceURL .= url
order .= 0
maxOrder .= size inner
p <- use profileName
profiles . contains p .= True
ast <- annotate inner
lift . liftF $ TS (AS { asToken = tok, asProfile = p }) (Source ty url df update) ast ()
token += 1
size :: Script Actions a -> Int
size = (`execState` 0) . go . evalScript def
where
go :: Free (Term Annotate Actions) a -> State Int ()
go (Free c@(TA {})) = id %= succ >> go (copoint c)
go (Free c@(TM {})) = go (copoint c)
go (Pure _) = return ()
actioned :: (FilePath -> FilePath -> Action) -> Script Actions ()
actioned f = Script $ do
rfp <- use app
sfp <- use source
url <- use sourceURL
o <- order <+= 1
mo <- use maxOrder
lift . liftF $ TA (AA { aaURI = url, aaOrder = o, aaMaxOrder = mo }) (f rfp sfp) ()
constructDestinationFilepath :: FilePath -> FilePath -> FilePath -> FilePath
constructDestinationFilepath r s d = execState ?? r $ do
id </>= d
when ("/" `isSuffixOf` d) $
id </>= (s^.filename)