opt-env-conf 0.12.2.0 → 0.13.0.0
raw patch · 9 files changed
+195/−96 lines, 9 filesdep +validity-textPVP ok
version bump matches the API change (PVP)
Dependencies added: validity-text
API changes (from Hackage documentation)
- OptEnvConf.Parser: instance Data.String.IsString OptEnvConf.Parser.Capability
- OptEnvConf.Parser: instance Data.Validity.Validity OptEnvConf.Parser.Capability
- OptEnvConf.Parser: instance GHC.Classes.Eq OptEnvConf.Parser.Capability
- OptEnvConf.Parser: instance GHC.Classes.Ord OptEnvConf.Parser.Capability
- OptEnvConf.Parser: instance GHC.Generics.Generic OptEnvConf.Parser.Capability
- OptEnvConf.Parser: instance GHC.Show.Show OptEnvConf.Parser.Capability
- OptEnvConf.Parser: requireCapability :: HasCallStack => String -> Parser a -> Parser a
- OptEnvConf.Run: Capabilities :: Set Capability -> Capabilities
- OptEnvConf.Run: [unCapabilities] :: Capabilities -> Set Capability
- OptEnvConf.Run: allCapabilities :: Capabilities
- OptEnvConf.Run: disableCapability :: Capability -> Capabilities -> Capabilities
- OptEnvConf.Run: enableCapability :: Capability -> Capabilities -> Capabilities
- OptEnvConf.Run: instance Data.Validity.Validity OptEnvConf.Run.Capabilities
- OptEnvConf.Run: instance GHC.Generics.Generic OptEnvConf.Run.Capabilities
- OptEnvConf.Run: instance GHC.Show.Show OptEnvConf.Run.Capabilities
- OptEnvConf.Run: newtype Capabilities
+ OptEnvConf: checkWithRequiredCapability :: HasCallStack => String -> Parser a -> Parser a
+ OptEnvConf: readSecretCapability :: String
+ OptEnvConf: requiredCapability :: String -> Builder a
+ OptEnvConf.Capability: Capabilities :: Set Capability -> Capabilities
+ OptEnvConf.Capability: Capability :: Text -> Capability
+ OptEnvConf.Capability: [unCapabilities] :: Capabilities -> Set Capability
+ OptEnvConf.Capability: [unCapability] :: Capability -> Text
+ OptEnvConf.Capability: allCapabilities :: Capabilities
+ OptEnvConf.Capability: disableCapability :: Capability -> Capabilities -> Capabilities
+ OptEnvConf.Capability: enableCapability :: Capability -> Capabilities -> Capabilities
+ OptEnvConf.Capability: instance Data.String.IsString OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: instance Data.Validity.Validity OptEnvConf.Capability.Capabilities
+ OptEnvConf.Capability: instance Data.Validity.Validity OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: instance GHC.Classes.Eq OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: instance GHC.Classes.Ord OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: instance GHC.Generics.Generic OptEnvConf.Capability.Capabilities
+ OptEnvConf.Capability: instance GHC.Generics.Generic OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: instance GHC.Show.Show OptEnvConf.Capability.Capabilities
+ OptEnvConf.Capability: instance GHC.Show.Show OptEnvConf.Capability.Capability
+ OptEnvConf.Capability: missingCapabilities :: Capabilities -> Set Capability -> Maybe (NonEmpty Capability)
+ OptEnvConf.Capability: newtype Capabilities
+ OptEnvConf.Capability: newtype Capability
+ OptEnvConf.Capability: readSecretCapability :: String
+ OptEnvConf.Parser: checkWithRequiredCapability :: HasCallStack => String -> Parser a -> Parser a
+ OptEnvConf.Setting: BuildAddRequiredCapability :: !Capability -> BuildInstruction a
+ OptEnvConf.Setting: [settingRequiredCapabilities] :: Setting a -> !Set Capability
+ OptEnvConf.Setting: readSecretCapability :: String
+ OptEnvConf.Setting: requiredCapability :: String -> Builder a
- OptEnvConf.Setting: Setting :: ![Dashed] -> ![Reader a] -> !Bool -> !Maybe a -> !Bool -> !Maybe (NonEmpty EnvVarSetting) -> !Maybe (NonEmpty (ConfigValSetting a)) -> !Maybe (a, String) -> ![String] -> !Bool -> !Maybe Metavar -> !Maybe String -> !Maybe Completer -> Setting a
+ OptEnvConf.Setting: Setting :: ![Dashed] -> ![Reader a] -> !Bool -> !Maybe a -> !Bool -> !Maybe (NonEmpty EnvVarSetting) -> !Maybe (NonEmpty (ConfigValSetting a)) -> !Maybe (a, String) -> ![String] -> !Bool -> !Maybe Metavar -> !Maybe String -> !Maybe Completer -> !Set Capability -> Setting a
Files
- CHANGELOG.md +14/−0
- opt-env-conf.cabal +3/−1
- src/OptEnvConf.hs +5/−0
- src/OptEnvConf/Capability.hs +62/−0
- src/OptEnvConf/Check.hs +1/−0
- src/OptEnvConf/Main.hs +1/−0
- src/OptEnvConf/Parser.hs +26/−32
- src/OptEnvConf/Run.hs +35/−40
- src/OptEnvConf/Setting.hs +48/−23
CHANGELOG.md view
@@ -1,5 +1,19 @@ # Changelog +## [0.13.0.0] - 2025-11-18++### Added++* Capability support for settings.++This is technically a breaking change, but if you don't use any `opt-env-conf`+internals, nothing should break for you.++### Changed++* Renamed `requireCapability` to `checkWithRequiredCapability`.+* Fixed that `showSettingABit` did not show every part of a setting.+ ## [0.12.2.0] - 2025-11-18 ### Changed
opt-env-conf.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: opt-env-conf-version: 0.12.2.0+version: 0.13.0.0 synopsis: Settings parsing for Haskell: command-line arguments, environment variables, and configuration values. homepage: https://github.com/NorfairKing/opt-env-conf#readme bug-reports: https://github.com/NorfairKing/opt-env-conf/issues@@ -26,6 +26,7 @@ exposed-modules: OptEnvConf OptEnvConf.Args+ OptEnvConf.Capability OptEnvConf.Casing OptEnvConf.Check OptEnvConf.Completer@@ -67,6 +68,7 @@ , text , validity , validity-containers+ , validity-text default-language: Haskell2010 if !os(windows) build-depends:
src/OptEnvConf.hs view
@@ -37,6 +37,7 @@ hidden, metavar, completer,+ requiredCapability, -- ** Commands commands,@@ -85,6 +86,7 @@ checkMapIOForgivable, checkMapMaybeForgivable, checkMapMaybe,+ checkWithRequiredCapability, mapIO, choice, withDefault,@@ -112,6 +114,7 @@ readSecretTextFile, secretTextFileSetting, secretTextFileOrBareSetting,+ readSecretCapability, -- *** Migration strOption,@@ -137,6 +140,7 @@ module OptEnvConf.Casing, module OptEnvConf.Doc, module OptEnvConf.Nix,+ module OptEnvConf.Capability, module OptEnvConf.Parser, module OptEnvConf.Completer, module OptEnvConf.Reader,@@ -147,6 +151,7 @@ where import Control.Applicative+import OptEnvConf.Capability import OptEnvConf.Casing import OptEnvConf.Completer import OptEnvConf.Doc
+ src/OptEnvConf/Capability.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module OptEnvConf.Capability+ ( Capabilities (..),+ Capability (..),+ allCapabilities,+ enableCapability,+ disableCapability,+ missingCapabilities,++ -- * Predefined capabilities+ readSecretCapability,+ )+where++import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE+import Data.Set (Set)+import qualified Data.Set as Set+import Data.String+import Data.Text (Text)+import Data.Validity+import Data.Validity.Containers ()+import Data.Validity.Text ()+import GHC.Generics (Generic)++-- Set of disabled capabilities+newtype Capabilities = Capabilities {unCapabilities :: Set Capability}+ deriving (Show, Generic)++instance Validity Capabilities++allCapabilities :: Capabilities+allCapabilities = Capabilities {unCapabilities = Set.empty}++enableCapability :: Capability -> Capabilities -> Capabilities+enableCapability cap (Capabilities caps) =+ Capabilities (Set.delete cap caps)++disableCapability :: Capability -> Capabilities -> Capabilities+disableCapability cap (Capabilities caps) =+ Capabilities (Set.insert cap caps)++missingCapabilities :: Capabilities -> Set Capability -> Maybe (NonEmpty Capability)+missingCapabilities (Capabilities caps) requiredCapabilities =+ NE.nonEmpty (Set.toList (Set.intersection requiredCapabilities caps))++newtype Capability = Capability {unCapability :: Text}+ deriving stock (Generic)+ deriving newtype (Show, Eq, Ord, IsString)++instance Validity Capability++-- | The annotation for any setting reading secrets.+--+-- We add these so that we can disable them in settings checks, to avoid+-- failing settings checks when secrets are read at runtime instead of+-- build-time.+readSecretCapability :: String+readSecretCapability = "read-secret"
src/OptEnvConf/Check.hs view
@@ -16,6 +16,7 @@ import GHC.Generics (Generic) import GHC.Stack (SrcLoc) import OptEnvConf.Args as Args+import OptEnvConf.Capability import OptEnvConf.EnvMap (EnvMap (..)) import OptEnvConf.Error import OptEnvConf.Parser
src/OptEnvConf/Main.hs view
@@ -11,6 +11,7 @@ import qualified Data.Text as T import Data.Version import OptEnvConf.Args as Args+import OptEnvConf.Capability import OptEnvConf.Check import OptEnvConf.Completion import OptEnvConf.Doc
src/OptEnvConf/Parser.hs view
@@ -1,8 +1,6 @@ {-# LANGUAGE ApplicativeDo #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}@@ -27,8 +25,8 @@ checkMapEitherForgivable, checkMapIOForgivable, checkMapMaybeForgivable,+ checkWithRequiredCapability, allOrNothing,- requireCapability, commands, command, defaultCommand,@@ -105,10 +103,9 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as T-import Data.Validity-import GHC.Generics (Generic) import GHC.Stack (HasCallStack, SrcLoc, callStack, getCallStack, withFrozenCallStack) import OptEnvConf.Args (Dashed (..), prefixDashed)+import OptEnvConf.Capability import OptEnvConf.Casing import OptEnvConf.Completer import OptEnvConf.Reader@@ -226,20 +223,6 @@ !(Setting a) -> Parser a -newtype Capability = Capability {unCapability :: Text}- deriving stock (Generic)- deriving newtype (Show, Eq, Ord, IsString)--instance Validity Capability---- | The annotation for any setting reading secrets.------ We add these so that we can disable them in settings checks, to avoid--- failing settings checks when secrets are read at runtime instead of--- build-time.-readSecretCapability :: String-readSecretCapability = "read-secret"- instance Functor Parser where -- We case-match to produce shallower parser structures. fmap f = \case@@ -669,13 +652,13 @@ -- that we can run a settings check without that power to still check the rest -- of the parser. ----- This only makes sense when used with a 'checkMap', 'checkEither', 'mapIO',--- 'runIO', or similar function because the capability requirement is attached--- to the Check node in the parser tree.+-- This only makes sense when used with a 'checkMap', 'checkEither',+-- 'mapIO', 'runIO', or similar function because the capability requirement is+-- attached to the Setting or Check node in the parser tree. -- When used without such a node, this will attach a no-op Check node to the -- parser tree and as such still try to do all the parsing below but not above.-requireCapability :: (HasCallStack) => String -> Parser a -> Parser a-requireCapability capName = \case+checkWithRequiredCapability :: (HasCallStack) => String -> Parser a -> Parser a+checkWithRequiredCapability capName = \case ParserCheck mLoc' forgivable caps f p -> ParserCheck mLoc' forgivable (Set.insert cap caps) f p p -> ParserCheck mLoc False (Set.singleton cap) (pure . Right) p@@ -921,7 +904,8 @@ settingHidden = True, settingMetavar = Nothing, settingHelp = Nothing,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } parseDisableSwitch :: Parser Bool parseDisableSwitch =@@ -939,7 +923,8 @@ settingHidden = True, settingMetavar = Nothing, settingHelp = Nothing,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } parseEnv :: Maybe (Parser Bool)@@ -960,7 +945,8 @@ settingHidden = False, settingMetavar = Just "BOOL", settingHelp = settingHelp s,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } parseConfigVal :: Maybe (Parser Bool) parseConfigVal = do@@ -980,7 +966,8 @@ settingHidden = False, settingMetavar = Nothing, settingHelp = settingHelp s,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } parseDummy :: Parser Bool parseDummy =@@ -998,7 +985,8 @@ settingHidden = False, settingMetavar = Nothing, settingHelp = settingHelp s,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } prefixDashedLong :: String -> Dashed -> Maybe Dashed prefixDashedLong prefix = \case@@ -1014,7 +1002,9 @@ secretTextFileSetting :: (HasCallStack) => [Builder FilePath] -> Parser Text secretTextFileSetting bs = withFrozenCallStack $- requireCapability readSecretCapability $+ -- Require the capability only for reading the secret file, not for parsing+ -- the string as a path.+ checkWithRequiredCapability readSecretCapability $ mapIO readSecretTextFile $ filePathSetting bs @@ -1038,12 +1028,16 @@ bareSetting p f = do let s = completeBuilder $ mconcat [mapMaybeBuilder f b, reader str, metavar "SECRET"] guard $ p s- pure $ T.pack <$> ParserSetting mLoc s+ pure $+ -- Require the capability for the entire setting because the secret may be+ -- passed as an env var.+ checkWithRequiredCapability readSecretCapability $+ T.pack <$> ParserSetting mLoc s fileSetting p f = do let s = completeBuilder $ mconcat [mapMaybeBuilder f b, reader str, metavar "FILE_PATH"] guard $ p s pure $- requireCapability readSecretCapability $+ checkWithRequiredCapability readSecretCapability $ -- These two mapIOs are not combined because the capability should -- only apply to the reading, not the resolving. mapIO readSecretTextFile $
src/OptEnvConf/Run.hs view
@@ -1,19 +1,15 @@ {-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-} module OptEnvConf.Run- ( Capabilities (..),- allCapabilities,- enableCapability,- disableCapability,- runParserOn,+ ( runParserOn, runHelpParser, ) where@@ -38,10 +34,9 @@ import qualified Data.Set as Set import qualified Data.Text as T import Data.Traversable-import Data.Validity (Validity)-import GHC.Generics (Generic) import GHC.Stack (SrcLoc) import OptEnvConf.Args as Args+import OptEnvConf.Capability import OptEnvConf.Doc import OptEnvConf.EnvMap (EnvMap (..)) import qualified OptEnvConf.EnvMap as EnvMap@@ -55,27 +50,6 @@ import System.IO import Text.Colour --- Set of disabled capabilities-newtype Capabilities = Capabilities {unCapabilities :: Set Capability}- deriving (Show, Generic)--instance Validity Capabilities--allCapabilities :: Capabilities-allCapabilities = Capabilities {unCapabilities = Set.empty}--enableCapability :: Capability -> Capabilities -> Capabilities-enableCapability cap (Capabilities caps) =- Capabilities (Set.delete cap caps)--disableCapability :: Capability -> Capabilities -> Capabilities-disableCapability cap (Capabilities caps) =- Capabilities (Set.insert cap caps)--missingCapabilities :: Capabilities -> Set Capability -> Maybe (NonEmpty Capability)-missingCapabilities (Capabilities caps) requiredCapabilities =- NE.nonEmpty (Set.toList (Set.intersection requiredCapabilities caps))- -- | Run a parser on given arguments and environment instead of getting them -- from the current process. runParserOn ::@@ -214,13 +188,8 @@ a <- ppIndent $ go p' debug ["check"] -- Only perform the check (IO) if capabilities are sufficient- ppIndent $ case missingCapabilities capabilities requiredCapabilities of- Just missings -> do- debug $- "Missing capabilities: "- : capabilitiesChunks (Set.fromList (NE.toList missings))- ppErrors mLoc $ NE.map ParseErrorMissingCapability missings- Nothing -> do+ ppIndent $+ withCapabilities mLoc requiredCapabilities capabilities $ do errOrB <- liftIO $ f a case errOrB of Left err -> do@@ -263,6 +232,17 @@ go pa ParserSetting mLoc set@Setting {..} -> do debug [syntaxChunk "Setting", ": ", mSrcLocChunk mLoc]+ when (not (Set.null settingRequiredCapabilities)) $+ debug $+ "Requires capabilities: "+ : capabilitiesChunks settingRequiredCapabilities++ -- Any argument parsing needs to still try to consume the relevant arguments, so we need to put+ -- this helper function after that instead of arround the whole setting parsing.+ -- After all the arguments, options, and switches have been tried, the rest can have a blanket 'cap'.+ let cap :: forall r. PP r -> PP r+ cap = withCapabilities mLoc settingRequiredCapabilities capabilities+ ppIndent $ do let markParsed = do maybe@@ -289,7 +269,7 @@ Nothing -> do debug ["could not set based on argument: no argument"] pure NotFound- Just argStr -> do+ Just argStr -> cap $ case tryReaders rs argStr of Left errs -> ppError mLoc $ ParseErrorArgumentRead mOptDoc errs Right a -> do@@ -316,7 +296,7 @@ chunk $ T.pack $ show $ map renderDashed settingDasheds ] pure NotFound- Just () -> do+ Just () -> cap $ do debug ["set based on switch."] pure $ Found a @@ -339,7 +319,7 @@ chunk $ T.pack $ show $ map renderDashed settingDasheds ] pure NotFound- Just optionStr -> do+ Just optionStr -> cap $ case tryReaders rs optionStr of Left err -> ppError mLoc $ ParseErrorOptionRead mOptDoc err Right a -> do@@ -356,7 +336,7 @@ pure a _ -> do let mEnvDoc = settingEnvDoc set- mEnv <- case settingEnvVars of+ mEnv <- cap $ case settingEnvVars of Nothing -> pure NotRun Just ne -> do -- Require readers before finding the env vars so the parser@@ -472,6 +452,21 @@ parseResultError (ParseErrorMissingEnvVar mEnvDoc) mEnv, parseResultError (ParseErrorMissingConfVal mConfDoc) mConf ]++withCapabilities ::+ Maybe SrcLoc ->+ Set Capability ->+ Capabilities ->+ PP a ->+ PP a+withCapabilities mLoc requiredCapabilities capabilities func =+ case missingCapabilities capabilities requiredCapabilities of+ Just missings -> do+ debug $+ "Missing capabilities: "+ : capabilitiesChunks (Set.fromList (NE.toList missings))+ ppErrors mLoc $ NE.map ParseErrorMissingCapability missings+ Nothing -> func data ParseResult a = NotRun
src/OptEnvConf/Setting.hs view
@@ -1,8 +1,10 @@+{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-}+{-# OPTIONS_GHC -Wno-unused-pattern-binds #-} module OptEnvConf.Setting ( Setting (..),@@ -37,6 +39,8 @@ completer, Builder (..), BuildInstruction (..),+ requiredCapability,+ readSecretCapability, -- * Internal showSettingABit,@@ -59,7 +63,11 @@ import Data.List.NonEmpty (NonEmpty (..), (<|)) import qualified Data.List.NonEmpty as NE import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.Text as T import OptEnvConf.Args (Dashed (..), renderDashed)+import OptEnvConf.Capability import OptEnvConf.Casing import OptEnvConf.Completer import OptEnvConf.Reader@@ -99,7 +107,8 @@ -- | Which metavar should be show in documentation settingMetavar :: !(Maybe Metavar), settingHelp :: !(Maybe String),- settingCompleter :: !(Maybe Completer)+ settingCompleter :: !(Maybe Completer),+ settingRequiredCapabilities :: !(Set Capability) } -- An 'Ord'-able Setting without giving 'Setting' an 'Eq' instance@@ -180,33 +189,43 @@ settingExamples = [], settingHidden = False, settingDefaultValue = Nothing,- settingCompleter = Nothing+ settingCompleter = Nothing,+ settingRequiredCapabilities = Set.empty } -- | Show a 'Setting' as much as possible, for debugging showSettingABit :: Setting a -> ShowS showSettingABit Setting {..} =- showParen True $- showString "Setting "- . showsPrec 11 settingDasheds- . showString " "- . showListWith (\_ -> showString "_") settingReaders- . showString " "- . showsPrec 11 settingTryArgument- . showString " "- . showMaybeWith (\_ -> showString "_") settingSwitchValue- . showString " "- . showsPrec 11 settingTryOption- . showString " "- . showsPrec 11 settingEnvVars- . showString " "- . showMaybeWith (showNonEmptyWith showConfigValSettingABit) settingConfigVals- . showString " "- . showMaybeWith (\_ -> showString "_") settingDefaultValue- . showString " "- . showsPrec 11 settingMetavar- . showString " "- . showsPrec 11 settingHelp+ let Setting _ _ _ _ _ _ _ _ _ _ _ _ _ _ = undefined+ in showParen True $+ showString "Setting "+ . showsPrec 11 settingDasheds+ . showString " "+ . showListWith (\_ -> showString "_") settingReaders+ . showString " "+ . showsPrec 11 settingTryArgument+ . showString " "+ . showMaybeWith (\_ -> showString "_") settingSwitchValue+ . showString " "+ . showsPrec 11 settingTryOption+ . showString " "+ . showsPrec 11 settingEnvVars+ . showString " "+ . showMaybeWith (showNonEmptyWith showConfigValSettingABit) settingConfigVals+ . showString " "+ . showMaybeWith (\_ -> showString "_") settingDefaultValue+ . showString " "+ . showsPrec 11 settingExamples+ . showString " "+ . showsPrec 11 settingHidden+ . showString " "+ . showsPrec 11 settingMetavar+ . showString " "+ . showsPrec 11 settingHelp+ . showString " "+ . showMaybeWith (\_ -> showString "_") settingCompleter+ . showString " "+ . showsPrec 11 settingRequiredCapabilities showConfigValSettingABit :: ConfigValSetting a -> ShowS showConfigValSettingABit ConfigValSetting {..} =@@ -244,6 +263,7 @@ | BuildAddExample !String | BuildSetHidden | BuildSetCompleter !Completer+ | BuildAddRequiredCapability !Capability applyBuildInstructions :: [BuildInstruction a] -> Setting a -> Setting a applyBuildInstructions is s = foldr applyBuildInstruction s is@@ -264,6 +284,7 @@ BuildAddExample e -> s {settingExamples = e : settingExamples s} BuildSetHidden -> s {settingHidden = True} BuildSetCompleter c -> s {settingCompleter = Just c}+ BuildAddRequiredCapability c -> s {settingRequiredCapabilities = Set.insert c (settingRequiredCapabilities s)} instance Semigroup (Builder f) where (<>) (Builder f1) (Builder f2) = Builder (f1 <> f2)@@ -446,3 +467,7 @@ -- Multiple 'completer's are redundant. completer :: Completer -> Builder a completer c = Builder [BuildSetCompleter c]++-- | Annotate a setting with a required capability.+requiredCapability :: String -> Builder a+requiredCapability c = Builder [BuildAddRequiredCapability (Capability (T.pack c))]