Charter.hs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. {-# LANGUAGE RecordWildCards #-}
  2. {-# LANGUAGE OverloadedStrings #-}
  3. module Charter
  4. ( module Types
  5. , projectBin
  6. , quickBin
  7. , library
  8. , mkBinary
  9. , projectDefaults
  10. , createProject
  11. , usualDeps
  12. ) where
  13. import Control.Monad (forM_)
  14. import qualified Data.Char as Char
  15. import Data.Monoid ((<>))
  16. import qualified Data.Text as T
  17. import qualified Data.Text.IO as T
  18. import Lens.Family
  19. import qualified System.Directory as Sys
  20. import qualified System.Environment as Sys
  21. import qualified System.FilePath as Sys
  22. import qualified System.Process as Proc
  23. import Types
  24. import Templates
  25. mkBinary :: T.Text -> ExecutableDetails
  26. mkBinary n = ExecutableDetails
  27. { _execName = n
  28. , _execDir = n
  29. , _execDeps = []
  30. }
  31. mkLibrary :: LibraryDetails
  32. mkLibrary = LibraryDetails
  33. { _libMods = []
  34. , _libDeps = []
  35. }
  36. mkProject :: ProjectDetails -> Project
  37. mkProject deets = Project
  38. { _projectDetails = deets
  39. , _libDetails = Nothing
  40. , _binDetails = []
  41. , _projectRoot = Nothing
  42. }
  43. quickBin :: ProjectDetails -> Project
  44. quickBin deets =
  45. mkProject deets & binDetails .~ [ bin ]
  46. where bin = mkBinary (deets^.projectName)
  47. & execDir .~ "src"
  48. projectBin :: ProjectDetails -> Project
  49. projectBin deets =
  50. mkProject deets & binDetails .~ [ bin ]
  51. & libDetails .~ Just lib
  52. where bin = mkBinary name & execDeps .~ [name]
  53. lib = mkLibrary & libMods .~ [capitalize name]
  54. name = deets^.projectName
  55. usualDeps :: [T.Text]
  56. usualDeps =
  57. [ "text"
  58. , "containers"
  59. , "unordered-containers"
  60. , "bytestring"
  61. ]
  62. library :: ProjectDetails -> Project
  63. library deets =
  64. mkProject deets & libDetails .~ Just mkLibrary
  65. mkdirBase :: T.Text -> [T.Text] -> IO ()
  66. mkdirBase base fp = do
  67. let path = T.unpack (T.intercalate "/" (base:fp))
  68. Sys.createDirectoryIfMissing True path
  69. writeBase :: T.Text -> [T.Text] -> T.Text -> IO ()
  70. writeBase base fp contents = do
  71. mkdirBase base (init fp)
  72. let path = T.unpack (T.intercalate "/" (base:fp))
  73. putStrLn ("- creating file `" <> path <> "'")
  74. T.writeFile path contents
  75. run :: String -> [String] -> IO T.Text
  76. run x xs = (T.strip . T.pack) `fmap` Proc.readProcess x xs ""
  77. projectDefaults :: T.Text -> IO ProjectDetails
  78. projectDefaults _projectName = do
  79. _projectAuthor <- run "git" ["config", "user.name"]
  80. _projectEmail <- run "git" ["config", "user.email"]
  81. _projectYear <- run "date" ["+%Y"]
  82. let _projectCategory = Nothing
  83. _projectSynopsis = Nothing
  84. _projectDescription = Nothing
  85. _projectLicense = Nothing
  86. return ProjectDetails { .. }
  87. -- | Capitalize just the first letter of a string
  88. capitalize :: T.Text -> T.Text
  89. capitalize t = case T.uncons t of
  90. Nothing -> mempty
  91. Just (x, xs) -> T.cons (Char.toUpper x) xs
  92. -- | Actually build out the scaffolding for a project
  93. createProject :: Project -> IO ()
  94. createProject pr = do
  95. let deets = pr^.projectDetails
  96. let write = writeBase (deets^.projectName)
  97. let cabalFile =
  98. [ cabalHeader deets ] <>
  99. maybe [] (pure . cabalLibrary) (pr^.libDetails) <>
  100. map cabalExecutable (pr^.binDetails)
  101. T.putStrLn ("Creating project `" <> deets^.projectName <> "'")
  102. let cabalPath = [deets^.projectName <> ".cabal"]
  103. write cabalPath (T.unlines cabalFile)
  104. case (pr^.libDetails) of
  105. Nothing -> return ()
  106. Just lib -> do
  107. forM_ (lib^.libMods) $ \ m -> do
  108. let modPath = "src" : T.splitOn "." m
  109. modPath' = init modPath ++ [last modPath <> ".hs"]
  110. write modPath' (defaultLib m)
  111. forM_ (pr^.binDetails) $ \e -> do
  112. write [e^.execDir, "Main.hs"] defaultBin
  113. let pr = (Proc.proc "git" ["init"])
  114. { Proc.cwd = Just (T.unpack (deets^.projectName)) }
  115. _ <- Proc.withCreateProcess pr (\_ _ _ -> Proc.waitForProcess)
  116. write [".gitignore"] defaultGitignore
  117. return ()