|
@@ -1,7 +1,113 @@
|
|
-module Collage.Commands.Splice (runSplice) where
|
|
|
|
|
|
+{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
+
|
|
|
|
+module Collage.Commands.Splice (runSplice, findFragments) where
|
|
|
|
+
|
|
|
|
+import Control.Monad (foldM)
|
|
|
|
+import qualified Data.Map as M
|
|
|
|
+import qualified Data.Text as T
|
|
|
|
+import qualified Data.Text.Lazy as TL
|
|
|
|
+import qualified Data.Text.Lazy.IO as TL
|
|
|
|
+import qualified System.Exit as Sys
|
|
|
|
+import System.FilePath ((</>))
|
|
|
|
+import qualified System.FilePath as Sys
|
|
|
|
|
|
import Collage.Config
|
|
import Collage.Config
|
|
import Collage.Opts
|
|
import Collage.Opts
|
|
|
|
|
|
|
|
+findFragmentIdentifiers :: TL.Text -> [T.Text]
|
|
|
|
+findFragmentIdentifiers t
|
|
|
|
+ | (_, rs) <- TL.break (== '«') t
|
|
|
|
+ , (f, rs') <- TL.break (== '»') (TL.drop 1 rs)
|
|
|
|
+ , not (TL.null f) = TL.toStrict f : findFragmentIdentifiers rs'
|
|
|
|
+ | otherwise = []
|
|
|
|
+
|
|
|
|
+toIdent :: T.Text -> TL.Text
|
|
|
|
+toIdent = TL.fromStrict . T.cons '«' . flip T.snoc '»'
|
|
|
|
+
|
|
|
|
+findFragments :: FilePath -> T.Text -> [Source] -> Either String (IO TL.Text)
|
|
|
|
+findFragments root s sources = case (T.splitOn "/" s) of
|
|
|
|
+ [] -> error "(should not happen)"
|
|
|
|
+ (x:rs) -> case [ src
|
|
|
|
+ | src@Source { sourceName = n } <- sources
|
|
|
|
+ , n == x
|
|
|
|
+ ] of
|
|
|
|
+ [src] -> go src rs (sourceExpose src)
|
|
|
|
+ [] -> Left ("Unable to find source named " ++ T.unpack x)
|
|
|
|
+ _ -> Left ("Ambiguous source name: " ++ T.unpack x)
|
|
|
|
+ where
|
|
|
|
+ go :: Source -> [T.Text] -> Expose -> Either String (IO TL.Text)
|
|
|
|
+ go q [] (ExposeFile path) =
|
|
|
|
+ return (TL.readFile (root </> sourceDir q </> path))
|
|
|
|
+ go _ [] ExposeSections{} =
|
|
|
|
+ Left $ concat [ "Splice identifier «"
|
|
|
|
+ , T.unpack s
|
|
|
|
+ , "» matches a file with sections, but "
|
|
|
|
+ , "does not specify a section!"
|
|
|
|
+ ]
|
|
|
|
+ go _ [] NamedMap{} =
|
|
|
|
+ Left $ concat [ "Splice identifier «"
|
|
|
|
+ , T.unpack s
|
|
|
|
+ , "» matches a map, but does not specify "
|
|
|
|
+ , "a key!"
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ go q [section] (ExposeSections path) =
|
|
|
|
+ return (readSection (root </> sourceDir q </> path) section)
|
|
|
|
+
|
|
|
|
+ go _ (_:_) (ExposeSections path) =
|
|
|
|
+ Left $ concat [ "Splice identifier «"
|
|
|
|
+ , T.unpack s
|
|
|
|
+ , "» indexes too far into the path "
|
|
|
|
+ , path
|
|
|
|
+ , "!"
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ go _ (_:_) (ExposeFile path) =
|
|
|
|
+ Left $ concat [ "Splice identifier «"
|
|
|
|
+ , T.unpack s
|
|
|
|
+ , "» indexes too far into the file "
|
|
|
|
+ , path
|
|
|
|
+ , "!"
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ go q (k:rs) (NamedMap m)
|
|
|
|
+ | Just e <- M.lookup k m = go q rs e
|
|
|
|
+ | otherwise = Left $ concat
|
|
|
|
+ [ "Splice identifier «"
|
|
|
|
+ , T.unpack s
|
|
|
|
+ , "» references a key "
|
|
|
|
+ , T.unpack k
|
|
|
|
+ , " which cannot be found."
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+doReplacements :: FilePath -> [Source] -> TL.Text -> IO TL.Text
|
|
|
|
+doReplacements root e original =
|
|
|
|
+ foldM doReplace original (findFragmentIdentifiers original)
|
|
|
|
+ where
|
|
|
|
+ doReplace file frag = do
|
|
|
|
+ new <- case findFragments root frag e of
|
|
|
|
+ Left err -> Sys.die err
|
|
|
|
+ Right x -> x
|
|
|
|
+ return (TL.replace (toIdent frag) new file)
|
|
|
|
+
|
|
|
|
+readSection :: FilePath -> T.Text -> IO TL.Text
|
|
|
|
+readSection path f = do
|
|
|
|
+ contents <- TL.readFile path
|
|
|
|
+ case TL.breakOn (toIdent f) contents of
|
|
|
|
+ (_, "") -> Sys.die $ unwords [ "Unable to find section"
|
|
|
|
+ , show f
|
|
|
|
+ , "in file"
|
|
|
|
+ , path
|
|
|
|
+ ]
|
|
|
|
+ (_, rs) ->
|
|
|
|
+ let (_, skipLine) = TL.breakOn "\n" rs
|
|
|
|
+ (section, _) = TL.breakOn "«end»" (TL.drop 1 skipLine)
|
|
|
|
+ (skipLine', _) = TL.breakOnEnd "\n" section
|
|
|
|
+ in return (TL.init skipLine')
|
|
|
|
+
|
|
runSplice :: Config -> Options -> IO ()
|
|
runSplice :: Config -> Options -> IO ()
|
|
-runSplice _ _ = putStrLn "Unimplemented!"
|
|
|
|
|
|
+runSplice conf opts = do
|
|
|
|
+ let root = Sys.takeDirectory (optFile opts)
|
|
|
|
+ f <- TL.readFile (root </> confDocument conf)
|
|
|
|
+ rs <- doReplacements root (confSources conf) f
|
|
|
|
+ TL.putStrLn rs
|