Charter.hs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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 :: T.Text -> LibraryDetails
  32. mkLibrary n = LibraryDetails
  33. { _libExposedModules = [capitalize n]
  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 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 lib
  65. where
  66. lib = mkLibrary (deets^.projectName)
  67. mkdirBase :: T.Text -> [T.Text] -> IO ()
  68. mkdirBase base fp = do
  69. let path = T.unpack (T.intercalate "/" (base:fp))
  70. Sys.createDirectoryIfMissing True path
  71. writeBase :: T.Text -> [T.Text] -> T.Text -> IO ()
  72. writeBase base fp contents = do
  73. mkdirBase base (init fp)
  74. let path = T.unpack (T.intercalate "/" (base:fp))
  75. putStrLn ("- creating file `" <> path <> "'")
  76. T.writeFile path contents
  77. run :: String -> [String] -> IO T.Text
  78. run x xs = (T.strip . T.pack) `fmap` Proc.readProcess x xs ""
  79. projectDefaults :: T.Text -> IO ProjectDetails
  80. projectDefaults _projectName = do
  81. _projectAuthor <- run "git" ["config", "user.name"]
  82. _projectEmail <- run "git" ["config", "user.email"]
  83. _projectYear <- run "date" ["+%Y"]
  84. let _projectCategory = Nothing
  85. _projectSynopsis = Nothing
  86. _projectDescription = Nothing
  87. _projectLicense = Nothing
  88. return ProjectDetails { .. }
  89. -- | Capitalize just the first letter of a string
  90. capitalize :: T.Text -> T.Text
  91. capitalize t = case T.uncons t of
  92. Nothing -> mempty
  93. Just (x, xs) -> T.cons (Char.toUpper x) xs
  94. -- | Actually build out the scaffolding for a project
  95. createProject :: Project -> IO ()
  96. createProject pr = do
  97. let deets = pr^.projectDetails
  98. let write = writeBase (deets^.projectName)
  99. let cabalFile =
  100. [ cabalHeader deets ] <>
  101. maybe [] (pure . cabalLibrary) (pr^.libDetails) <>
  102. map cabalExecutable (pr^.binDetails)
  103. T.putStrLn ("Creating project `" <> deets^.projectName <> "'")
  104. let cabalPath = [deets^.projectName <> ".cabal"]
  105. write cabalPath (T.unlines cabalFile)
  106. case (pr^.libDetails) of
  107. Nothing -> return ()
  108. Just lib -> do
  109. forM_ (lib^.libExposedModules) $ \ m -> do
  110. let modPath = "src" : T.splitOn "." (m <> ".hs")
  111. write modPath (defaultLib m)
  112. forM_ (pr^.binDetails) $ \e -> do
  113. write [e^.execDir, "Main.hs"] defaultBin
  114. let pr = (Proc.proc "git" ["init"])
  115. { Proc.cwd = Just (T.unpack (deets^.projectName)) }
  116. _ <- Proc.withCreateProcess pr (\_ _ _ -> Proc.waitForProcess)
  117. write [".gitignore"] defaultGitignore
  118. return ()