mirror of
https://github.com/anoma/juvix.git
synced 2024-12-13 11:16:48 +03:00
4fcb881ebc
- Closes #2067 This pr adds the field `main` to `juvix.yaml`. This field is optional and should contain a path to a juvix file that is meant to be used for the `compile` (and `dev compile`) command when no file is given as an argument in the CLI. This makes it possible to simply run `juvix compile` if the `main` is specified in the `jvuix.yaml`. I have updated the `juvix.yaml` of the milestone examples. --------- Co-authored-by: Paul Cadman <git@paulcadman.dev> Co-authored-by: Jonathan Cubides <jonathan.cubides@uib.no>
138 lines
4.0 KiB
Haskell
138 lines
4.0 KiB
Haskell
module Commands.Init where
|
|
|
|
import Data.Text qualified as Text
|
|
import Data.Versions
|
|
import Data.Yaml (encodeFile)
|
|
import Juvix.Compiler.Pipeline.Package
|
|
import Juvix.Data.Effect.Fail.Extra qualified as Fail
|
|
import Juvix.Extra.Paths
|
|
import Juvix.Prelude
|
|
import Juvix.Prelude.Pretty
|
|
import Text.Megaparsec (Parsec)
|
|
import Text.Megaparsec qualified as P
|
|
import Text.Megaparsec.Char qualified as P
|
|
|
|
type Err = Text
|
|
|
|
parse :: Parsec Void Text a -> Text -> Either Err a
|
|
parse p t = mapLeft ppErr (P.runParser p "<stdin>" t)
|
|
|
|
ppErr :: P.ParseErrorBundle Text Void -> Text
|
|
ppErr = pack . errorBundlePretty
|
|
|
|
init :: forall r. (Members '[Embed IO] r) => Sem r ()
|
|
init = do
|
|
checkNotInProject
|
|
say "✨ Your next Juvix adventure is about to begin! ✨"
|
|
say "I will help you set it up"
|
|
pkg <- getPackage
|
|
say ("creating " <> pack (toFilePath juvixYamlFile))
|
|
embed (encodeFile (toFilePath juvixYamlFile) (rawPackage pkg))
|
|
say "you are all set"
|
|
|
|
checkNotInProject :: forall r. (Members '[Embed IO] r) => Sem r ()
|
|
checkNotInProject =
|
|
whenM (doesFileExist juvixYamlFile) err
|
|
where
|
|
err :: Sem r ()
|
|
err = do
|
|
say "You are already in a Juvix project"
|
|
embed exitFailure
|
|
|
|
getPackage :: forall r. Members '[Embed IO] r => Sem r Package
|
|
getPackage = do
|
|
tproj <- getProjName
|
|
say "Write the version of your project [leave empty for 0.0.0]"
|
|
tversion :: SemVer <- getVersion
|
|
return
|
|
Package
|
|
{ _packageName = tproj,
|
|
_packageVersion = tversion,
|
|
_packageBuildDir = Nothing,
|
|
_packageMain = Nothing,
|
|
_packageDependencies = [defaultStdlibDep]
|
|
}
|
|
|
|
getProjName :: forall r. (Members '[Embed IO] r) => Sem r Text
|
|
getProjName = do
|
|
d <- getDefault
|
|
let defMsg :: Text
|
|
defMsg = case d of
|
|
Nothing -> mempty
|
|
Just d' -> " [leave empty for '" <> d' <> "']"
|
|
say
|
|
( "Write the name of your project"
|
|
<> defMsg
|
|
<> " (lower case letters, numbers and dashes are allowed): "
|
|
)
|
|
readName d
|
|
where
|
|
getDefault :: Sem r (Maybe Text)
|
|
getDefault = runFail $ do
|
|
dir <- map toLower . dropTrailingPathSeparator . toFilePath . dirname <$> getCurrentDir
|
|
Fail.fromRight (parse projectNameParser (pack dir))
|
|
readName :: Maybe Text -> Sem r Text
|
|
readName def = go
|
|
where
|
|
go :: Sem r Text
|
|
go = do
|
|
txt <- embed getLine
|
|
if
|
|
| Text.null txt, Just def' <- def -> return def'
|
|
| otherwise ->
|
|
case parse projectNameParser txt of
|
|
Right p
|
|
| Text.length p <= projextNameMaxLength -> return p
|
|
| otherwise -> do
|
|
say ("The project name cannot exceed " <> prettyText projextNameMaxLength <> " characters")
|
|
retry
|
|
Left err -> do
|
|
say err
|
|
retry
|
|
where
|
|
retry :: Sem r Text
|
|
retry = do
|
|
tryAgain
|
|
go
|
|
|
|
say :: (Members '[Embed IO] r) => Text -> Sem r ()
|
|
say = embed . putStrLn
|
|
|
|
tryAgain :: (Members '[Embed IO] r) => Sem r ()
|
|
tryAgain = say "Please, try again:"
|
|
|
|
getVersion :: forall r. (Members '[Embed IO] r) => Sem r SemVer
|
|
getVersion = do
|
|
txt <- embed getLine
|
|
if
|
|
| Text.null txt -> return mempty
|
|
| otherwise -> case parse semver' txt of
|
|
Right r -> return r
|
|
Left err -> do
|
|
say err
|
|
say "The version must follow the 'Semantic Versioning 2.0.0' specification"
|
|
retry
|
|
where
|
|
retry :: Sem r SemVer
|
|
retry = do
|
|
tryAgain
|
|
getVersion
|
|
|
|
projextNameMaxLength :: Int
|
|
projextNameMaxLength = 100
|
|
|
|
projectNameParser :: Parsec Void Text Text
|
|
projectNameParser = do
|
|
h <- P.satisfy validFirstChar
|
|
t <- P.takeWhileP (Just "project name character") validChar
|
|
P.hspace
|
|
P.eof
|
|
return (Text.cons h t)
|
|
where
|
|
validFirstChar :: Char -> Bool
|
|
validFirstChar c =
|
|
isAscii c
|
|
&& (isLower c || isNumber c)
|
|
validChar :: Char -> Bool
|
|
validChar c = c == '-' || validFirstChar c
|