diff --git a/src/Generator.hs b/src/Generator.hs index cb3f5795e..af95ca8f4 100644 --- a/src/Generator.hs +++ b/src/Generator.hs @@ -5,7 +5,7 @@ module Generator import CompileOptions (CompileOptions) import Wasp import Generator.Generators (generateWebApp) -import Generator.FileDraft (FileDraft, writeToFile) +import Generator.FileDraft (FileDraft, write) -- | Generates web app code from given Wasp and writes it to given destination directory. @@ -20,4 +20,4 @@ writeWebAppCode wasp dstDir compileOptions = writeFileDrafts dstDir (generateWeb -- TODO(martin): We could/should parallelize this. -- We could also skip writing files that are already on the disk with same checksum. writeFileDrafts :: FilePath -> [FileDraft] -> IO () -writeFileDrafts dstDir fileDrafts = sequence_ $ map (writeToFile dstDir) fileDrafts +writeFileDrafts dstDir fileDrafts = sequence_ $ map (write dstDir) fileDrafts diff --git a/src/Generator/FileDraft.hs b/src/Generator/FileDraft.hs index 6f4c06a5f..2dbf01cb6 100644 --- a/src/Generator/FileDraft.hs +++ b/src/Generator/FileDraft.hs @@ -1,6 +1,6 @@ module Generator.FileDraft ( FileDraft(..) - , WriteableToFile(..) + , Writeable(..) , createTemplateFileDraft , createCopyFileDraft , createTextFileDraft @@ -10,7 +10,7 @@ module Generator.FileDraft import qualified Data.Aeson as Aeson import Data.Text (Text) -import Generator.FileDraft.WriteableToFile +import Generator.FileDraft.Writeable import Generator.FileDraft.TemplateFileDraft (TemplateFileDraft) import qualified Generator.FileDraft.TemplateFileDraft as TemplateFD @@ -35,11 +35,11 @@ data FileDraft | FileDraftCopyDirDraft CopyDirDraft deriving (Show, Eq) -instance WriteableToFile FileDraft where - writeToFile dstDir (FileDraftTemplateFd draft) = writeToFile dstDir draft - writeToFile dstDir (FileDraftCopyFd draft) = writeToFile dstDir draft - writeToFile dstDir (FileDraftTextFd draft) = writeToFile dstDir draft - writeToFile dstDir (FileDraftCopyDirDraft draft) = writeToFile dstDir draft +instance Writeable FileDraft where + write dstDir (FileDraftTemplateFd draft) = write dstDir draft + write dstDir (FileDraftCopyFd draft) = write dstDir draft + write dstDir (FileDraftTextFd draft) = write dstDir draft + write dstDir (FileDraftCopyDirDraft draft) = write dstDir draft createTemplateFileDraft :: FilePath -> FilePath -> Aeson.Value -> FileDraft diff --git a/src/Generator/FileDraft/CopyDirDraft.hs b/src/Generator/FileDraft/CopyDirDraft.hs index f39cf061e..3dfd21c10 100644 --- a/src/Generator/FileDraft/CopyDirDraft.hs +++ b/src/Generator/FileDraft/CopyDirDraft.hs @@ -4,8 +4,8 @@ module Generator.FileDraft.CopyDirDraft import System.FilePath (FilePath, ()) -import Generator.FileDraft.WriteableToFile -import Generator.FileDraft.FileDraftIO +import Generator.FileDraft.Writeable +import Generator.FileDraft.WriteableMonad -- | File draft based purely on another dir, that is just copied recursively with all the files in it. data CopyDirDraft = CopyDirDraft @@ -16,7 +16,7 @@ data CopyDirDraft = CopyDirDraft } deriving (Show, Eq) -instance WriteableToFile CopyDirDraft where - writeToFile dstDir draft = do +instance Writeable CopyDirDraft where + write dstDir draft = do let dstAbsPath = dstDir (dstPath draft) copyDirectory (srcPath draft) dstAbsPath diff --git a/src/Generator/FileDraft/CopyFileDraft.hs b/src/Generator/FileDraft/CopyFileDraft.hs index cc35128d1..c7533b727 100644 --- a/src/Generator/FileDraft/CopyFileDraft.hs +++ b/src/Generator/FileDraft/CopyFileDraft.hs @@ -4,8 +4,8 @@ module Generator.FileDraft.CopyFileDraft import System.FilePath (FilePath, (), takeDirectory) -import Generator.FileDraft.WriteableToFile -import Generator.FileDraft.FileDraftIO +import Generator.FileDraft.Writeable +import Generator.FileDraft.WriteableMonad -- | File draft based purely on another file, that is just copied. data CopyFileDraft = CopyFileDraft @@ -17,8 +17,8 @@ data CopyFileDraft = CopyFileDraft } deriving (Show, Eq) -instance WriteableToFile CopyFileDraft where - writeToFile dstDir (CopyFileDraft dstFilepath srcFilepath) = do +instance Writeable CopyFileDraft where + write dstDir (CopyFileDraft dstFilepath srcFilepath) = do let dstAbsFilepath = dstDir dstFilepath srcAbsFilepath <- getTemplateFileAbsPath srcFilepath createDirectoryIfMissing True (takeDirectory dstAbsFilepath) diff --git a/src/Generator/FileDraft/TemplateFileDraft.hs b/src/Generator/FileDraft/TemplateFileDraft.hs index dc30f8961..e8c308eaf 100644 --- a/src/Generator/FileDraft/TemplateFileDraft.hs +++ b/src/Generator/FileDraft/TemplateFileDraft.hs @@ -6,8 +6,8 @@ import System.FilePath (FilePath, (), takeDirectory) import Data.Text (Text) import qualified Data.Aeson as Aeson -import Generator.FileDraft.WriteableToFile -import Generator.FileDraft.FileDraftIO +import Generator.FileDraft.Writeable +import Generator.FileDraft.WriteableMonad -- | File draft based on template file that gets combined with data. @@ -21,8 +21,8 @@ data TemplateFileDraft = TemplateFileDraft } deriving (Show, Eq) -instance WriteableToFile TemplateFileDraft where - writeToFile dstDir draft = +instance Writeable TemplateFileDraft where + write dstDir draft = compileAndRenderTemplate templateRelFilepath templateData >>= writeContentToFile where templateRelFilepath :: FilePath @@ -31,7 +31,7 @@ instance WriteableToFile TemplateFileDraft where templateData :: Aeson.Value templateData = templateFileDraftTemplateData draft - writeContentToFile :: (FileDraftIO m) => Text -> m () + writeContentToFile :: (WriteableMonad m) => Text -> m () writeContentToFile content = do let absDstFilepath = dstDir (templateFileDraftDstFilepath draft) createDirectoryIfMissing True (takeDirectory absDstFilepath) diff --git a/src/Generator/FileDraft/TextFileDraft.hs b/src/Generator/FileDraft/TextFileDraft.hs index 47667adcb..2108be1e7 100644 --- a/src/Generator/FileDraft/TextFileDraft.hs +++ b/src/Generator/FileDraft/TextFileDraft.hs @@ -4,8 +4,8 @@ module Generator.FileDraft.TextFileDraft import System.FilePath (FilePath, (), takeDirectory) -import Generator.FileDraft.WriteableToFile -import Generator.FileDraft.FileDraftIO +import Generator.FileDraft.Writeable +import Generator.FileDraft.WriteableMonad import Data.Text (Text) @@ -18,8 +18,8 @@ data TextFileDraft = TextFileDraft } deriving (Show, Eq) -instance WriteableToFile TextFileDraft where - writeToFile dstDir (TextFileDraft dstFilepath content) = do +instance Writeable TextFileDraft where + write dstDir (TextFileDraft dstFilepath content) = do let dstAbsFilepath = dstDir dstFilepath createDirectoryIfMissing True (takeDirectory dstAbsFilepath) writeFileFromText dstAbsFilepath content diff --git a/src/Generator/FileDraft/Writeable.hs b/src/Generator/FileDraft/Writeable.hs new file mode 100644 index 000000000..d4aadee90 --- /dev/null +++ b/src/Generator/FileDraft/Writeable.hs @@ -0,0 +1,12 @@ +module Generator.FileDraft.Writeable + ( Writeable(..) + ) where + +import Generator.FileDraft.WriteableMonad + +class Writeable w where + -- | Write file somewhere in the provided dst directory. + write :: (WriteableMonad m) + => FilePath -- ^ Absolute path of dst directory. + -> w + -> m () diff --git a/src/Generator/FileDraft/FileDraftIO.hs b/src/Generator/FileDraft/WriteableMonad.hs similarity index 93% rename from src/Generator/FileDraft/FileDraftIO.hs rename to src/Generator/FileDraft/WriteableMonad.hs index 18431da36..f20799241 100644 --- a/src/Generator/FileDraft/FileDraftIO.hs +++ b/src/Generator/FileDraft/WriteableMonad.hs @@ -1,5 +1,5 @@ -module Generator.FileDraft.FileDraftIO - ( FileDraftIO(..) +module Generator.FileDraft.WriteableMonad + ( WriteableMonad(..) ) where @@ -16,7 +16,7 @@ import qualified Generator.Templates -- https://news.ycombinator.com/item?id=10392044 -- | Describes effects needed by File Drafts. -class (Monad m) => FileDraftIO m where +class (Monad m) => WriteableMonad m where createDirectoryIfMissing :: Bool -- ^ True if parents should also be created. -> FilePath -- ^ Path to the directory to create. @@ -51,7 +51,7 @@ class (Monad m) => FileDraftIO m where -> Aeson.Value -- ^ JSON to be provided as template data. -> m Text -instance FileDraftIO IO where +instance WriteableMonad IO where createDirectoryIfMissing = System.Directory.createDirectoryIfMissing copyFile = System.Directory.copyFile writeFileFromText = Data.Text.IO.writeFile diff --git a/src/Generator/FileDraft/WriteableToFile.hs b/src/Generator/FileDraft/WriteableToFile.hs deleted file mode 100644 index ad69f8f2b..000000000 --- a/src/Generator/FileDraft/WriteableToFile.hs +++ /dev/null @@ -1,14 +0,0 @@ -module Generator.FileDraft.WriteableToFile - ( WriteableToFile(..) - ) where - -import Generator.FileDraft.FileDraftIO - -class WriteableToFile w where - -- | Based on "WriteableToFile" instance, creates file somewhere in the provided dst - -- directory. - writeToFile - :: (FileDraftIO m) - => FilePath -- ^ Absolute path of dst directory. - -> w -- ^ "WriteableToFile" instance to be written to file. - -> m () diff --git a/test/Generator/FileDraft/CopyFileDraftTest.hs b/test/Generator/FileDraft/CopyFileDraftTest.hs index 1712003d5..61fdbfacb 100644 --- a/test/Generator/FileDraft/CopyFileDraftTest.hs +++ b/test/Generator/FileDraft/CopyFileDraftTest.hs @@ -6,22 +6,22 @@ import System.FilePath ((), takeDirectory) import Generator.FileDraft -import Generator.MockFileDraftIO +import qualified Generator.MockWriteableMonad as Mock spec_CopyFileDraft :: Spec spec_CopyFileDraft = do - describe "writeToFile" $ do + describe "write" $ do it "Creates new file by copying existing file" $ do - let mock = writeToFile dstDir fileDraft - let mockLogs = getMockLogs mock defaultMockConfig - createDirectoryIfMissing_calls mockLogs + let mock = write dstDir fileDraft + let mockLogs = Mock.getMockLogs mock Mock.defaultMockConfig + Mock.createDirectoryIfMissing_calls mockLogs `shouldBe` [(True, takeDirectory expectedDstPath)] - copyFile_calls mockLogs + Mock.copyFile_calls mockLogs `shouldBe` [(expectedSrcPath, expectedDstPath)] where (dstDir, dstPath, srcPath) = ("a/b", "c/d/dst.txt", "e/src.txt") fileDraft = createCopyFileDraft dstPath srcPath expectedSrcPath = mockTemplatesDirAbsPath srcPath expectedDstPath = dstDir dstPath - mockTemplatesDirAbsPath = getTemplatesDirAbsPath_impl defaultMockConfig + mockTemplatesDirAbsPath = Mock.getTemplatesDirAbsPath_impl Mock.defaultMockConfig diff --git a/test/Generator/FileDraft/TemplateFileDraftTest.hs b/test/Generator/FileDraft/TemplateFileDraftTest.hs index 784fcda48..accac0baf 100644 --- a/test/Generator/FileDraft/TemplateFileDraftTest.hs +++ b/test/Generator/FileDraft/TemplateFileDraftTest.hs @@ -8,20 +8,20 @@ import Data.Text (Text) import Generator.FileDraft -import Generator.MockFileDraftIO +import qualified Generator.MockWriteableMonad as Mock spec_TemplateFileDraft :: Spec spec_TemplateFileDraft = do - describe "writeToFile" $ do + describe "write" $ do it "Creates new file from existing template file" $ do - let mock = writeToFile dstDir fileDraft - let mockLogs = getMockLogs mock mockConfig - compileAndRenderTemplate_calls mockLogs + let mock = write dstDir fileDraft + let mockLogs = Mock.getMockLogs mock mockConfig + Mock.compileAndRenderTemplate_calls mockLogs `shouldBe` [(templatePath, templateData)] - createDirectoryIfMissing_calls mockLogs + Mock.createDirectoryIfMissing_calls mockLogs `shouldBe` [(True, takeDirectory expectedDstPath)] - writeFileFromText_calls mockLogs + Mock.writeFileFromText_calls mockLogs `shouldBe` [(expectedDstPath, mockTemplateContent)] where (dstDir, dstPath, templatePath) = ("a/b", "c/d/dst.txt", "e/tmpl.txt") @@ -30,7 +30,7 @@ spec_TemplateFileDraft = do expectedDstPath = dstDir dstPath mockTemplatesDirAbsPath = "mock/templates/dir" mockTemplateContent = "Mock template content" :: Text - mockConfig = defaultMockConfig - { getTemplatesDirAbsPath_impl = mockTemplatesDirAbsPath - , compileAndRenderTemplate_impl = \_ _ -> mockTemplateContent + mockConfig = Mock.defaultMockConfig + { Mock.getTemplatesDirAbsPath_impl = mockTemplatesDirAbsPath + , Mock.compileAndRenderTemplate_impl = \_ _ -> mockTemplateContent } diff --git a/test/Generator/GeneratorsTest.hs b/test/Generator/GeneratorsTest.hs index e933e3ee2..6415db78f 100644 --- a/test/Generator/GeneratorsTest.hs +++ b/test/Generator/GeneratorsTest.hs @@ -73,7 +73,7 @@ spec_Generators = do existsFdWithDst :: [FileDraft] -> FilePath -> Bool existsFdWithDst fds dstPath = any ((== dstPath) . getFileDraftDstPath) fds --- TODO(martin): This should really become part of the WriteableToFile typeclass, +-- TODO(martin): This should really become part of the Writeable typeclass, -- since it is smth we want to do for all file drafts. getFileDraftDstPath :: FileDraft -> FilePath getFileDraftDstPath (FileDraftTemplateFd fd) = templateFileDraftDstFilepath fd diff --git a/test/Generator/MockFileDraftIO.hs b/test/Generator/MockWriteableMonad.hs similarity index 63% rename from test/Generator/MockFileDraftIO.hs rename to test/Generator/MockWriteableMonad.hs index b61438a09..7ae1fed98 100644 --- a/test/Generator/MockFileDraftIO.hs +++ b/test/Generator/MockWriteableMonad.hs @@ -1,9 +1,9 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} -module Generator.MockFileDraftIO - ( MockFdIO - , MockFdIOLogs(..) - , MockFdIOConfig(..) +module Generator.MockWriteableMonad + ( MockWriteableMonad + , MockWriteableMonadLogs(..) + , MockWriteableMonadConfig(..) , getMockLogs , defaultMockConfig ) where @@ -13,46 +13,46 @@ import Data.Text (Text, pack) import Control.Monad.State import qualified Data.Aeson as Aeson -import Generator.FileDraft.FileDraftIO +import Generator.FileDraft.WriteableMonad -- TODO: Instead of manually defining mock like this, consider using monad-mock package, -- it should do most of this automatically, now there is a lot of boilerplate. -- Or we ourselves can maybe use template haskell to reduce duplication. -defaultMockConfig :: MockFdIOConfig -defaultMockConfig = MockFdIOConfig +defaultMockConfig :: MockWriteableMonadConfig +defaultMockConfig = MockWriteableMonadConfig { getTemplatesDirAbsPath_impl = "mock/templates/dir" , getTemplateFileAbsPath_impl = \path -> "mock/templates/dir" path , compileAndRenderTemplate_impl = \_ _ -> (pack "Mock template content") } -getMockLogs :: MockFdIO a -> MockFdIOConfig -> MockFdIOLogs -getMockLogs mock config = fst $ execState (unMockFdIO mock) (emptyLogs, config) +getMockLogs :: MockWriteableMonad a -> MockWriteableMonadConfig -> MockWriteableMonadLogs +getMockLogs mock config = fst $ execState (unMockWriteableMonad mock) (emptyLogs, config) where - emptyLogs = MockFdIOLogs [] [] [] [] [] [] + emptyLogs = MockWriteableMonadLogs [] [] [] [] [] [] -instance FileDraftIO MockFdIO where - writeFileFromText dstPath text = MockFdIO $ do +instance WriteableMonad MockWriteableMonad where + writeFileFromText dstPath text = MockWriteableMonad $ do modifyLogs (writeFileFromText_addCall dstPath text) - getTemplatesDirAbsPath = MockFdIO $ do + getTemplatesDirAbsPath = MockWriteableMonad $ do modifyLogs getTemplatesDirAbsPath_addCall (_, config) <- get return $ getTemplatesDirAbsPath_impl config - createDirectoryIfMissing createParents path = MockFdIO $ do + createDirectoryIfMissing createParents path = MockWriteableMonad $ do modifyLogs (createDirectoryIfMissing_addCall createParents path) - copyFile srcPath dstPath = MockFdIO $ do + copyFile srcPath dstPath = MockWriteableMonad $ do modifyLogs (copyFile_addCall srcPath dstPath) - getTemplateFileAbsPath path = MockFdIO $ do + getTemplateFileAbsPath path = MockWriteableMonad $ do modifyLogs (getTemplateFileAbsPath_addCall path) (_, config) <- get return $ (getTemplateFileAbsPath_impl config) path - compileAndRenderTemplate path json = MockFdIO $ do + compileAndRenderTemplate path json = MockWriteableMonad $ do modifyLogs (compileAndRenderTemplate_addCall path json) (_, config) <- get return $ (compileAndRenderTemplate_impl config) path json @@ -60,10 +60,12 @@ instance FileDraftIO MockFdIO where modifyLogs :: MonadState (a, b) m => (a -> a) -> m () modifyLogs f = modify (\(logs, config) -> (f logs, config)) -newtype MockFdIO a = MockFdIO { unMockFdIO :: State (MockFdIOLogs, MockFdIOConfig) a } +newtype MockWriteableMonad a = MockWriteableMonad + { unMockWriteableMonad :: State (MockWriteableMonadLogs, MockWriteableMonadConfig) a + } deriving (Monad, Applicative, Functor) -data MockFdIOLogs = MockFdIOLogs +data MockWriteableMonadLogs = MockWriteableMonadLogs { writeFileFromText_calls :: [(FilePath, Text)] , getTemplatesDirAbsPath_calls :: [()] , createDirectoryIfMissing_calls :: [(Bool, FilePath)] @@ -72,34 +74,34 @@ data MockFdIOLogs = MockFdIOLogs , compileAndRenderTemplate_calls :: [(FilePath, Aeson.Value)] } -data MockFdIOConfig = MockFdIOConfig +data MockWriteableMonadConfig = MockWriteableMonadConfig { getTemplatesDirAbsPath_impl :: FilePath , getTemplateFileAbsPath_impl :: FilePath -> FilePath , compileAndRenderTemplate_impl :: FilePath -> Aeson.Value -> Text } -writeFileFromText_addCall :: FilePath -> Text -> MockFdIOLogs -> MockFdIOLogs +writeFileFromText_addCall :: FilePath -> Text -> MockWriteableMonadLogs -> MockWriteableMonadLogs writeFileFromText_addCall path text logs = logs { writeFileFromText_calls = (path, text):(writeFileFromText_calls logs) } -getTemplatesDirAbsPath_addCall :: MockFdIOLogs -> MockFdIOLogs +getTemplatesDirAbsPath_addCall :: MockWriteableMonadLogs -> MockWriteableMonadLogs getTemplatesDirAbsPath_addCall logs = logs { getTemplatesDirAbsPath_calls = ():(getTemplatesDirAbsPath_calls logs) } -getTemplateFileAbsPath_addCall :: FilePath -> MockFdIOLogs -> MockFdIOLogs +getTemplateFileAbsPath_addCall :: FilePath -> MockWriteableMonadLogs -> MockWriteableMonadLogs getTemplateFileAbsPath_addCall path logs = logs { getTemplateFileAbsPath_calls = (path):(getTemplateFileAbsPath_calls logs) } -copyFile_addCall :: FilePath -> FilePath -> MockFdIOLogs -> MockFdIOLogs +copyFile_addCall :: FilePath -> FilePath -> MockWriteableMonadLogs -> MockWriteableMonadLogs copyFile_addCall srcPath dstPath logs = logs { copyFile_calls = (srcPath, dstPath):(copyFile_calls logs) } -createDirectoryIfMissing_addCall :: Bool -> FilePath -> MockFdIOLogs -> MockFdIOLogs +createDirectoryIfMissing_addCall :: Bool -> FilePath -> MockWriteableMonadLogs -> MockWriteableMonadLogs createDirectoryIfMissing_addCall createParents path logs = logs { createDirectoryIfMissing_calls = (createParents, path):(createDirectoryIfMissing_calls logs) } -compileAndRenderTemplate_addCall :: FilePath -> Aeson.Value -> MockFdIOLogs -> MockFdIOLogs +compileAndRenderTemplate_addCall :: FilePath -> Aeson.Value -> MockWriteableMonadLogs -> MockWriteableMonadLogs compileAndRenderTemplate_addCall path json logs = logs { compileAndRenderTemplate_calls = (path, json):(compileAndRenderTemplate_calls logs) }