elm-init 1.0.0.0 → 1.0.0.1
raw patch · 5 files changed
+240/−2 lines, 5 files
Files
- elm-init.cabal +6/−2
- src/ElmInit.hs +15/−0
- src/ElmInit/Interact.hs +71/−0
- src/ElmInit/Types.hs +110/−0
- src/ElmInit/Util.hs +38/−0
elm-init.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: elm-init-version: 1.0.0.0+version: 1.0.0.1 synopsis: Set up basic structure for an elm project description: Initialize a new empty elm project with some basic scaffolding according to 'https://github.com/evancz/elm-architecture-tutorial'.@@ -28,7 +28,11 @@ executable elm-init main-is: Main.hs- -- other-modules:+ other-modules:+ ElmInit+ ElmInit.Types+ ElmInit.Interact+ ElmInit.Util -- other-extensions: build-depends: base >=4.8 && <5.0,
+ src/ElmInit.hs view
@@ -0,0 +1,15 @@+module ElmInit+ ( UserDecisions(..)+ , CmdArgs(..)+ , Result+ , askChoices+ , askChoicesWithOther+ , exists+ , verifyElmVersion+ , makePackage+ , flattenMaybe+ ) where++import ElmInit.Interact+import ElmInit.Types+import ElmInit.Util
+ src/ElmInit/Interact.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE OverloadedStrings #-}++module ElmInit.Interact+ ( askChoicesWithOther+ , askChoices+ ) where+++import qualified Control.Arrow as Arrow (first)+import Control.Applicative ((<$>), (<*>))+import Data.Text (append)+import Data.Bool (bool)+import ElmInit.Util (getOr, enumerate)+import Data.Text as Text (Text, intercalate, pack)+import Data.Text.IO as TextIO (getLine, putStrLn)+import Prelude hiding (getLine, putStrLn)+++askChoices :: Text -> Int -> [Text] -> IO Text+askChoices =+ ((fmap <$> (!!) <*>) .) . askChoices'+++askChoices' :: Text -> Int -> [Text] -> IO Int+askChoices' message selected choices =+ putStrLn message >>+ ask out++ where+ (l1, selectedElem : l2tail) = splitAt selected choices+ out =+ intercalate+ "\n"+ (normFormat 1 l1+ ++ (selectedFormat selected selectedElem : normFormat (selected + 1) l2tail))++ enumF = flip append " ) " . pack . show+ enumFn = append " " . enumF+ enumFs = append " * " . enumF+ normFormat = (map (uncurry append . Arrow.first enumFn) .) . enumerate+ selectedFormat x y = (flip append y . enumFs) x++ ask a =+ putStrLn a >>+ getOr selected >>=+ (bool+ (putStrLn "invalid choice, please choose again" >>+ ask a)+ <$> return+ <*> (<= length choices))+++askChoicesWithOther :: Text -> Int -> (Text -> Either Text a) -> [Text] -> IO a+askChoicesWithOther m s trans l =+ askChoices' m s (l ++ ["other (specify)"])+ >>= (flip bool getAlternative+ <$> either (const $ error "No parse") return . trans . (l !!)+ <*> (== length l))++ where+ getAlternative =+ putStrLn "please enter an alternative" >>+ getLine >>=+ (either+ (\message ->+ putStrLn "Invalid input:" >>+ putStrLn message >>+ getAlternative+ )+ return+ . trans)
+ src/ElmInit/Types.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE OverloadedStrings #-}++module ElmInit.Types+ ( Result+ , CmdArgs(..)+ , UserDecisions(..)+ , ElmPackage(..)+ , readVersion+ , makePackage+ , verifyElmVersion+ , readOneVersion+ ) where+++import Data.Aeson as Aeson (ToJSON, object, (.=),+ toJSON, Value)+import Control.Applicative ((<*>), (<$>))+import Control.Monad ((<=<))+import Data.Version (Version(Version), makeVersion,+ showVersion, parseVersion)+import Data.Text (pack, Text)+import Text.ParserCombinators.ReadP (readP_to_S)++++type Result = Either Text ()+++data CmdArgs = CmdArgs { workingDirectory :: FilePath }+++data UserDecisions = Default { projectName :: Text+ , sourceFolder :: FilePath+ , version :: Version+ , summary :: Text+ , repository :: Text+ , license :: Text+ , elmVersion :: Text+ , mainFileName :: String+ , addIndex :: Bool+ }+++data ElmPackage = ElmPackage { pkgVersion :: Version+ , pkgSummary :: Text+ , pkgRepository :: Text+ , pkgLicense :: Text+ , pkgDependencies :: Aeson.Value+ , pkgExposedModules :: [Text]+ , pkgElmVersion :: Text+ , pkgSourceDirs :: [Text]+ }+++readVersion :: String -> [(Version, String)]+readVersion = readP_to_S parseVersion+++instance Aeson.ToJSON ElmPackage where+ toJSON = object . sequenceA+ [ ("version" .=) . showVersion . pkgVersion+ , ("summary" .=) . pkgSummary+ , ("repository" .=) . pkgRepository+ , ("license" .=) . pkgLicense+ , ("dependencies" .=) . pkgDependencies+ , ("exposed-modules" .=) . pkgExposedModules+ , ("elm-version" .=) . pkgElmVersion+ , ("source-directories".=). pkgSourceDirs+ ]+++emptyDecisions :: UserDecisions+emptyDecisions =+ Default { summary = ""+ , repository = ""+ , version = makeVersion [0, 0, 0]+ , license = ""+ , sourceFolder = ""+ , projectName = ""+ , elmVersion = ""+ , mainFileName = "Main.elm"+ , addIndex = True+ }+++makePackage :: UserDecisions -> ElmPackage+makePackage = ElmPackage+ <$> version+ <*> summary+ <*> repository+ <*> license+ <*> const (object [("elm-lang/core", "2.0.0 <= v < 3.0.0")])+ <*> const []+ <*> elmVersion+ <*> (:[]) . pack . sourceFolder+++readOneVersion :: String -> Either Text Version+readOneVersion = verif . readVersion+ where+ verif ((v, []):_) = return v+ verif [] = Left "Version must have this structure: 1.2.3"+ verif (_:xs) = verif xs+++verifyElmVersion :: String -> Either Text Version+verifyElmVersion = hasElmStructure <=< readOneVersion+ where+ hasElmStructure v@(Version [ _, _, _ ] []) = Right v+ hasElmStructure _ = Left "Version must have this structure: 1.2.3"
+ src/ElmInit/Util.hs view
@@ -0,0 +1,38 @@+module ElmInit.Util+ ( flattenMaybe+ , exists+ , getOr+ , enumerate+ ) where+++import Control.Applicative ((<$>), (<*>))+import Data.Maybe (fromMaybe)+import Data.Bool (bool)+import System.Directory (doesFileExist, doesDirectoryExist)+import Control.Exception (catch, IOException)+++flattenMaybe :: Maybe (Maybe a) -> Maybe a+flattenMaybe = fromMaybe Nothing+++exists :: FilePath -> IO Bool+exists =+ (>>=)+ <$> doesFileExist+ <*> (flip bool+ (return True)+ . doesDirectoryExist)+++getOr :: Read a => a -> IO a+getOr =+ catch readLn . handler+ where+ handler :: a -> IOException -> IO a+ handler = const . return+++enumerate :: Int -> [a] -> [(Int,a)]+enumerate from l = zip [from..(length l)] l