Skip to content

Commit

Permalink
Merge branch 'pipe-placeholders'
Browse files Browse the repository at this point in the history
  • Loading branch information
myme committed May 10, 2024
2 parents 5a8d802 + 5b3540c commit 2855911
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 26 deletions.
95 changes: 70 additions & 25 deletions src/Nixon/Config/Markdown.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import Data.Maybe (fromMaybe, mapMaybe)
import Data.Text (pack, strip)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Data.Tuple (swap)
import qualified Data.Yaml as Yaml
import Nixon.Command (bg, json, (<!))
import qualified Nixon.Command as Cmd
Expand Down Expand Up @@ -279,40 +278,86 @@ parseCommandName = first (T.pack . show) . P.parse parser ""
parseCommandArgs :: Parser [Cmd.Placeholder]
parseCommandArgs =
P.choice
[ (:) <$> parseCommandArg' <*> parseCommandArgs,
[ (:) <$> parseCommandPlaceholder <*> parseCommandArgs,
P.anyChar *> parseCommandArgs,
[] <$ P.eof
]

-- | Convenience wrapper for running placeholder parser
parseCommandArg :: String -> Either String Cmd.Placeholder
parseCommandArg = first show . P.parse parseCommandArg' "" . T.pack
parseCommandArg = first show . P.parse parseCommandPlaceholder "" . T.pack

parseCommandArg' :: Parser Cmd.Placeholder
parseCommandArg' = do
parseCommandPlaceholder :: Parser Cmd.Placeholder
parseCommandPlaceholder = do
let startCmdArg =
(Cmd.Stdin <$ P.char '<')
<|> (Cmd.Arg <$ P.char '$')
<|> (Cmd.EnvVar . T.pack <$> P.many (P.alphaNum <|> P.char '_') <* P.char '=')
placeholderType <- P.try $ startCmdArg <* P.char '{'
(name, fields, multiple) <- parseSpec <* P.char '}'
let fixup = T.replace "-" "_"
placeholderWithName = case placeholderType of
Cmd.EnvVar "" -> Cmd.EnvVar $ fixup name
Cmd.EnvVar alias -> Cmd.EnvVar $ fixup alias
same -> same
pure $ Cmd.Placeholder placeholderWithName name fields multiple []

parseSpec :: Parser (Text, [Integer], Bool)
parseSpec = do
name <- T.pack <$> P.many1 (P.noneOf ":}")
P.option (name, [], False) $ do
_ <- P.char ':'
-- Accept fields and multiple in any order
(fields, multiple) <-
((,) <$> parseFields <*> parseMultiple)
<|> (fmap swap . (,) <$> parseMultiple <*> P.option [] parseFields)
pure (name, fields, multiple)
placeholder <- do
name <- T.pack <$> P.many1 (P.noneOf " :|}")
let fixup = T.replace "-" "_"
placeholderWithName = case placeholderType of
Cmd.EnvVar "" -> Cmd.EnvVar $ fixup name
Cmd.EnvVar alias -> Cmd.EnvVar $ fixup alias
same -> same
pure $ Cmd.Placeholder placeholderWithName name [] False []
parsePlaceholderModifiers placeholder <* P.char '}'

-- | Parse the "modifiers" which affect how command placeholders are handled.
--
-- This includes:
--
-- * If the placeholder can select multiple values.
-- * Which fields to include from selections.
-- * TODO: Interpret the input as JSON.
-- * TODO: Access JSON attributes through jq-like expressions.
--
-- Formats take on two types, shorthand and pipelines. The following are
-- equivalent:
--
-- Pipeline: `some-command ${placeholder | multiple | fields 1,3}`
-- Shorthand: `some-command ${placeholder:m1,3}`
parsePlaceholderModifiers :: Cmd.Placeholder -> Parser Cmd.Placeholder
parsePlaceholderModifiers placeholder = do
P.choice
[ parsePipeModifiers placeholder,
parseColonModifiers placeholder,
pure placeholder
]
where
parseMultiple = P.option False (True <$ P.char 'm')
parseFields = mapMaybe readMaybe <$> (P.many1 P.digit `P.sepBy1` P.char ',')
-- Parse `command-name | fields 1,2 | multiple`
parsePipeModifiers :: Cmd.Placeholder -> Parser Cmd.Placeholder
parsePipeModifiers p = do
_ <- P.many P.space *> P.char '|' *> P.many P.space
p' <-
P.choice
[ parsePipeFields p,
parsePipeMultiple p
]
_ <- P.many P.space
P.option p' (parsePipeModifiers p')

parsePipeFields p = P.string "fields" *> P.many P.space *> parseFields p

parsePipeMultiple p = (P.string "multi" :: Parser String) $> p {Cmd.multiple = True}

-- Parse `command-name:1,2`
parseColonModifiers :: Cmd.Placeholder -> Parser Cmd.Placeholder
parseColonModifiers p = do
_ <- P.char ':'
-- Accept fields and multiple in any order
(parseFields p >>= perhaps parseMultiple) <|> (parseMultiple p >>= perhaps parseFields)

parseFields :: Cmd.Placeholder -> Parser Cmd.Placeholder
parseFields p' = do
fields <- mapMaybe readMaybe <$> (P.many1 P.digit `P.sepBy1` P.char ',')
pure $ p' {Cmd.fields = fields}

parseMultiple :: Cmd.Placeholder -> Parser Cmd.Placeholder
parseMultiple p' = do
multiple <- P.option False (True <$ P.char 'm')
pure $ p' {Cmd.multiple = multiple}

-- Try a parser or default to `value`
perhaps parser value = P.option value (parser value)
23 changes: 22 additions & 1 deletion test/Test/Nixon/Config/Markdown.hs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@ command_tests = describe "commands section" $ do
"# `three`",
"``` bash ${arg-three:1,2}",
"echo Hello \"$1\"",
"```",
"# `four`",
"``` bash ${arg-four | fields 1,2 | multi}",
"echo Hello \"$1\"",
"```"
]
selector
Expand All @@ -436,7 +440,8 @@ command_tests = describe "commands section" $ do
`shouldBe` Right
[ (Bash, [Placeholder Arg "arg-one" [] False []]),
(Bash, [Placeholder Arg "arg-two" [] True []]),
(Bash, [Placeholder Arg "arg-three" [1, 2] False []])
(Bash, [Placeholder Arg "arg-three" [1, 2] False []]),
(Bash, [Placeholder Arg "arg-four" [1, 2] True []])
]

it "complains on both header & code block placeholders" $ do
Expand Down Expand Up @@ -523,6 +528,14 @@ parse_command_name_tests = describe "parseCommandName" $ do
parseCommandName "cat ${arg:m}"
`shouldBe` Right ("cat", [Placeholder Arg "arg" [] True []])

it "parses arg modifiers" $ do
parseCommandName "cat ${arg | fields 1,3}"
`shouldBe` Right ("cat", [Placeholder Arg "arg" [1, 3] False []])

it "parses arg modifiers" $ do
parseCommandName "cat ${arg | multi}"
`shouldBe` Right ("cat", [Placeholder Arg "arg" [] True []])

it "parses stdin arg modifiers" $ do
parseCommandName "cat <{arg:m}"
`shouldBe` Right ("cat", [Placeholder Stdin "arg" [] True []])
Expand All @@ -535,6 +548,14 @@ parse_command_name_tests = describe "parseCommandName" $ do
parseCommandName "cat <{arg:1,3,5m}"
`shouldBe` Right ("cat", [Placeholder Stdin "arg" [1, 3, 5] True []])

it "parses arg field and pipe fields" $ do
parseCommandName "cat <{arg | fields 1,3,5}"
`shouldBe` Right ("cat", [Placeholder Stdin "arg" [1, 3, 5] False []])

it "parses arg field, pipe fields and pipe multiple" $ do
parseCommandName "cat <{arg | fields 1,3,5 | multi}"
`shouldBe` Right ("cat", [Placeholder Stdin "arg" [1, 3, 5] True []])

it "parses text and placeholder part" $ do
parseCommandName "cat \"${arg}\""
`shouldBe` Right ("cat", [Placeholder Arg "arg" [] False []])
Expand Down

0 comments on commit 2855911

Please sign in to comment.