effectful-cli 0.2.6.0 → 0.2.7.0
raw patch · 5 files changed
+54/−11 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Data.CLI.Config: configKeys :: ConfigFile c => [Text]
- Effectful.CLI.Config: setConfigKey :: forall a (es :: [Effect]). (FromValue a, IOE :> es, Error CLIError :> es) => FilePath -> Text -> Text -> Eff es ()
+ Effectful.CLI.Config: setConfigKey :: forall a (es :: [Effect]). (FromValue a, IOE :> es, Error CLIError :> es) => [Text] -> FilePath -> Text -> Text -> Eff es ()
Files
- CHANGELOG.md +6/−0
- effectful-cli.cabal +1/−1
- src/Data/CLI/Config.hs +15/−6
- src/Effectful/CLI/Config.hs +15/−3
- test/Spec.hs +17/−1
CHANGELOG.md view
@@ -1,3 +1,9 @@+## [2.7.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.6.0...2.7.0) (2026-11-07)++### Features++* add config functions completions ([4ec43cf](https://github.com/haskell-github-trust/effectful-cli/commit/4ec43cf6177da343e4dc9258598fc131751da502))+ ## [2.6.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.5.0...2.6.0) (2026-11-06) ### Features
effectful-cli.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: effectful-cli-version: 0.2.6.0+version: 0.2.7.0 synopsis: CLI helpers for the effectful ecosystem description: Please see the README at https://github.com/haskell-github-trust/effectful-cli category: CLI
src/Data/CLI/Config.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} @@ -18,7 +19,7 @@ import Effectful.CLI.IO (printOutputLn) import Effectful.Reader.Static (asks) import GHC.Enum (boundedEnumFrom, boundedEnumFromThen)-import Options.Applicative (Parser, metavar, strArgument)+import Options.Applicative (Parser, completer, listCompleter, metavar, strArgument) import Toml.Schema (FromValue) -- | Application config @c@ that knows its config file and the type it decodes to.@@ -31,6 +32,11 @@ -- | Path of the config file. configFilePath :: c -> FilePath + -- | Known dotted keys, used for shell completion and for hinting when a+ -- section key is set to a scalar.+ configKeys :: [Text]+ configKeys = []+ -- | Config file commands for config @c@ and application error @e@. IO failures -- are raised as 'InputError'. --@@ -71,15 +77,18 @@ doc (ConfigSet _ _) = "Set a dotted key to a TOML literal, or a string if not one" parser ConfigShow = noArgs ConfigShow- parser (ConfigGet _) = ConfigGet <$> keyArg- parser (ConfigSet _ _) = ConfigSet <$> keyArg <*> strArgument (metavar "VALUE")+ parser (ConfigGet _) = ConfigGet <$> keyArg (configKeys @c)+ parser (ConfigSet _ _) = ConfigSet <$> keyArg (configKeys @c) <*> strArgument (metavar "VALUE") runner cmd = do path <- asks @c configFilePath rethrowIO (InputError . ("config file: " <>) . T.show) $ case cmd of ConfigShow -> printOutputLn . T.stripEnd =<< readConfigText path ConfigGet k -> printOutputLn =<< getConfigKey path k- ConfigSet k v -> setConfigKey @(ConfigOf c) path k v+ ConfigSet k v -> setConfigKey @(ConfigOf c) (configKeys @c) path k v -keyArg :: Parser Text-keyArg = strArgument (metavar "KEY")+keyArg+ :: [Text]+ -> Parser Text+keyArg keys =+ strArgument (metavar "KEY" <> completer (listCompleter (fmap T.unpack keys)))
src/Effectful/CLI/Config.hs view
@@ -67,26 +67,38 @@ -- tables; the file is only written if the result still decodes as @a@. -- Re-renders the whole file, dropping comments and formatting. --+-- When decoding fails and @key@ is a section of one of the known keys, the+-- error says which keys to set instead.+-- -- @since 0.2.6.0+--+-- The known-keys argument was added in 0.2.7.0. setConfigKey :: forall a es . FromValue a => IOE :> es => Error CLIError :> es- => FilePath+ => [Text]+ -> FilePath -> Text -> Text -> Eff es ()-setConfigKey path key raw = do+setConfigKey known path key raw = do table <- either (throwError . InputError) pure . setKey (T.splitOn "." key) (parseValue raw) =<< readTable path let rendered = T.show (prettyToml table) case Toml.decode @a rendered of- Failure es -> throwError (InputError ("invalid config: " <> T.pack (unlines es)))+ Failure es ->+ throwError+ (InputError ("invalid config: " <> T.stripEnd (T.pack (unlines es)) <> sectionHint)) Success _ _ -> liftIO $ do createDirectoryIfMissing True (takeDirectory path) T.writeFile path rendered+ where+ sectionHint = case filter (T.isPrefixOf (key <> ".")) known of+ [] -> ""+ ks -> "; " <> key <> " is a section, set one of: " <> T.intercalate ", " ks readTable :: IOE :> es
test/Spec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} import Data.CLI.Impl (@@ -24,6 +25,7 @@ import Test.Hspec import Text.Megaparsec.Char (string) import Toml (Table, Table' (..), Value' (..))+import Toml.Schema (FromValue (..), parseTableFromValue, reqKey, reqKeyOf) isInputError :: Either CLIError a -> Bool isInputError (Left (InputError _)) = True@@ -136,7 +138,21 @@ it "writes then reads a key through the file" $ do path <- (</> "effectful-cli-config-test.toml") <$> getTemporaryDirectory r <- runEff . runErrorNoCallStack @CLIError $ do- setConfigKey @Table path "a.b" "1"+ setConfigKey @Table [] path "a.b" "1" getConfigKey path "a.b" removeFile path r `shouldBe` Right "1"++ it "hints at known keys when a section is set to a scalar" $ do+ path <- (</> "effectful-cli-config-hint-test.toml") <$> getTemporaryDirectory+ r <-+ runEff . runErrorNoCallStack @CLIError $+ setConfigKey @Section ["sec.x", "sec.y", "other.z"] path "sec" "plain"+ r `shouldSatisfy` \case+ Left (InputError m) -> "sec is a section, set one of: sec.x, sec.y" `isInfixOf` show m+ _ -> False++newtype Section = Section Int++instance FromValue Section where+ fromValue = parseTableFromValue (Section <$> reqKeyOf "sec" (parseTableFromValue (reqKey "x")))