Charter.hs 4.0 KB

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