hercules-ci-cnix-expr 0.3.3.0 → 0.3.4.0
raw patch · 6 files changed
+121/−11 lines, 6 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Hercules.CNix.Expr: instance Hercules.CNix.Expr.FromValue Hercules.CNix.Expr.Typed.NixInt GHC.Int.Int64
+ Hercules.CNix.Expr: instance Hercules.CNix.Expr.FromValue Hercules.CNix.Expr.Typed.NixList [Hercules.CNix.Expr.Raw.RawValue]
+ Hercules.CNix.Expr.Schema: (#??) :: (KnownSymbol s, (as ? s) ~ (Null |. b), PossibleTypesForSchema b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b))
+ Hercules.CNix.Expr.Schema: InvalidValue :: Provenance -> Text -> NixException
+ Hercules.CNix.Expr.Schema: ListItem :: Provenance -> Int -> Provenance
+ Hercules.CNix.Expr.Schema: instance Hercules.CNix.Expr.Schema.FromPSObject GHC.Int.Int64 GHC.Int.Int64
+ Hercules.CNix.Expr.Schema: instance Hercules.CNix.Expr.Schema.FromPSObject a b => Hercules.CNix.Expr.Schema.FromPSObject [a] [b]
+ Hercules.CNix.Expr.Schema: instance Hercules.CNix.Expr.Schema.PossibleTypesForSchema ()
+ Hercules.CNix.Expr.Schema: instance Hercules.CNix.Expr.Schema.PossibleTypesForSchema [a]
+ Hercules.CNix.Expr.Schema: traverseArray :: MonadEval m => (PSObject a -> m b) -> PSObject [a] -> m [b]
+ Hercules.CNix.Expr.Schema: type as ? s = OptionalAttrType' as as s
+ Hercules.CNix.Expr.Typed: getInt :: Value NixInt -> IO Int64
+ Hercules.CNix.Expr.Typed: instance Hercules.CNix.Expr.Typed.HasRawValueType ()
- Hercules.CNix.Expr.Schema: (#.) :: (KnownSymbol s, AttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)
+ Hercules.CNix.Expr.Schema: (#.) :: (KnownSymbol s, (as . s) ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)
- Hercules.CNix.Expr.Schema: (#?!) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)
+ Hercules.CNix.Expr.Schema: (#?!) :: (KnownSymbol s, (as ? s) ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)
- Hercules.CNix.Expr.Schema: (#?) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b))
+ Hercules.CNix.Expr.Schema: (#?) :: (KnownSymbol s, (as ? s) ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b))
- Hercules.CNix.Expr.Schema: (>>.) :: (KnownSymbol s, AttrType as s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (PSObject b)
+ Hercules.CNix.Expr.Schema: (>>.) :: (KnownSymbol s, (as . s) ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (PSObject b)
- Hercules.CNix.Expr.Schema: (>>?) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (Maybe (PSObject b))
+ Hercules.CNix.Expr.Schema: (>>?) :: (KnownSymbol s, (as ? s) ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (Maybe (PSObject b))
- Hercules.CNix.Expr.Schema: type a ::? b = a :? b
+ Hercules.CNix.Expr.Schema: type a ::?? b = a :? Null |. b
Files
- CHANGELOG.md +14/−1
- hercules-ci-cnix-expr.cabal +1/−1
- src/Hercules/CNix/Expr.hs +9/−0
- src/Hercules/CNix/Expr/Schema.hs +55/−8
- src/Hercules/CNix/Expr/Typed.hs +8/−0
- test/Hercules/CNix/Expr/SchemaSpec.hs +34/−1
CHANGELOG.md view
@@ -5,7 +5,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## 0.3.3.0 - 2022-05-21+## 0.3.4.0 - 2022-11-15++### Added++ - `::??`, `#??` for redundantly optional types: unset or null+ - Schema module instances for `()` representing `null`+ - `InvalidValue` constructor for `NixException`+ - `.` and `?` type operators for field access and optional field access+ - Schema module instances for `[a]` for Nix lists of `a`,+ - Schema `Int64` (= `NixInt`) instance for Nix integers+ - Schema `traverseArray` helper for traversing Nix lists (should have been renamed)+++## 0.3.3.0 - 2022-06-21 ### Added
hercules-ci-cnix-expr.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: hercules-ci-cnix-expr-version: 0.3.3.0+version: 0.3.4.0 synopsis: Bindings for the Nix evaluator category: Nix, CI, Testing, DevOps homepage: https://docs.hercules-ci.com
src/Hercules/CNix/Expr.hs view
@@ -8,6 +8,9 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+#ifdef __GHCIDE__+# define NIX_IS_AT_LEAST(mm,m,p) 1+#endif module Hercules.CNix.Expr ( init,@@ -564,6 +567,12 @@ instance FromValue Bool Bool where fromValue = getBool++instance FromValue NixList [RawValue] where+ fromValue = getList++instance FromValue NixInt Int64 where+ fromValue = getInt -- | Identity instance ToRawValue RawValue where
src/Hercules/CNix/Expr/Schema.hs view
@@ -48,9 +48,12 @@ -- * Attribute sets as records type Attrs, type (::.),+ type (.), (#.), (>>.), type (::?),+ type (::??),+ type (?), (#?), (>>?), (#?!),@@ -77,6 +80,8 @@ -- * Utilities uncheckedCast, englishOr,+ traverseArray,+ (#??), ) where @@ -84,7 +89,7 @@ import qualified Data.Map as M import qualified Data.Text as T import qualified GHC.TypeLits as TL-import Hercules.CNix.Expr (CheckType, EvalState, HasRawValueType, NixAttrs, NixFunction, NixPath, NixString, RawValue, ToRawValue (..), ToValue (..), Value (rtValue), apply, checkType, getAttr, getRawValueType, getStringIgnoreContext, hasContext, rawValueType, toRawValue, valueFromExpressionString)+import Hercules.CNix.Expr (CheckType, EvalState, HasRawValueType, NixAttrs, NixFunction, NixList, NixPath, NixString, RawValue, ToRawValue (..), ToValue (..), Value (rtValue), apply, checkType, getAttr, getRawValueType, getStringIgnoreContext, hasContext, rawValueType, toRawValue, valueFromExpressionString) import qualified Hercules.CNix.Expr as Expr import Hercules.CNix.Expr.Raw (RawValueType, canonicalRawType) import Protolude hiding (TypeError, check, evalState)@@ -95,6 +100,7 @@ | Other Text | Data | Attribute Provenance Text+ | ListItem Provenance Int | Application Provenance Provenance deriving (Show, Eq, Ord) @@ -108,6 +114,7 @@ -- ^ expected | InvalidText Provenance UnicodeException | StringContextNotAllowed Provenance+ | InvalidValue Provenance Text deriving (Show, Eq) instance Exception NixException where@@ -115,9 +122,11 @@ displayException (TypeError p actual expected) = "Expecting a value of type " <> toS (englishOr (map show expected)) <> ", but got type " <> show actual <> "." <> appendProvenance p displayException (InvalidText p ue) = displayException ue <> appendProvenance p displayException (StringContextNotAllowed p) = "This string must not have a context. It must be usable without building store paths." <> appendProvenance p+ displayException (InvalidValue p msg) = "Invalid value. " <> toS msg <> appendProvenance p appendProvenance :: Provenance -> [Char] appendProvenance (Attribute p name) = "\n in attribute " <> show name <> appendProvenance p+appendProvenance (ListItem p index) = "\n in list item " <> show index <> appendProvenance p appendProvenance (Other x) = "\n in " <> toS x appendProvenance Data = "" appendProvenance (Application p _p) = "\n in function result" <> appendProvenance p@@ -136,6 +145,9 @@ infixr 1 ->? +-- | A Nix @null@ value has 1 possible value, like Haskell's @()@.+type Null = ()+ -- | Attribute set schema with known attributes and wildcard type for remaining attributes. data Attrs' (as :: [Attr]) w @@ -168,6 +180,12 @@ -- distinct from an attribute that may be @null@. type a ::? b = a ':? b +-- | Optional (@_?@) attribute name and type (@::_@)+--+-- This indicates that the attribute may be omitted in its entirety, which is+-- distinct from an attribute that may be @null@.+type a ::?? b = a ':? Null |. b+ -- | Required (@_.@) attribute name and type (@::_@) -- -- Note that the type may still be nullable, but the attribute is expected to exist.@@ -207,7 +225,7 @@ value = v } -type AttrType as s = AttrType' as as s+type as . s = AttrType' as as s type family AttrType' all as s where AttrType' all ((s ':. t) ': as) s = t@@ -223,7 +241,7 @@ 'TL.:$$: 'TL.Text " Known attributes are " 'TL.:<>: 'TL.ShowType all ) -type OptionalAttrType as s = OptionalAttrType' as as s+type as ? s = OptionalAttrType' as as s type family OptionalAttrType' all as s where OptionalAttrType' all ((s ':? t) ': as) s = t@@ -252,11 +270,11 @@ type MonadEval m = (MonadIO m, MonadReader (Ptr EvalState) m) -- | A combination of '>>=' and '#.'.-(>>.) :: (KnownSymbol s, AttrType as s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (PSObject b)+(>>.) :: (KnownSymbol s, as . s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (PSObject b) mas >>. p = mas >>= \as -> as #. p -- | Attribute selector. @a #. #b@ is @a.b@ in Nix. Operates on attributes that are required (@_.@) in the schema, throwing an error if necessary.-(#.) :: (KnownSymbol s, AttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)+(#.) :: (KnownSymbol s, as . s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b) as #. p = do evalState <- ask let name = T.pack (symbolVal p)@@ -266,11 +284,11 @@ Just b -> pure PSObject {value = b, provenance = Attribute (provenance as) name} -- | A combination of '>>=' and '#?'.-(>>?) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (Maybe (PSObject b))+(>>?) :: (KnownSymbol s, as ? s ~ b, MonadEval m) => m (PSObject (Attrs' as w)) -> AttrLabel s -> m (Maybe (PSObject b)) mas >>? p = mas >>= \as -> as #? p -- | Attribute selector. @a #? #b@ is @a.b@ in Nix, but handles the missing case without exception. Operates on attributes that are optional (@_?@) in the schema, throwing an error if necessary.-(#?) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b))+(#?) :: (KnownSymbol s, as ? s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b)) as #? p = do evalState <- ask let name = T.pack (symbolVal p)@@ -278,11 +296,17 @@ liftIO (getAttr evalState v (encodeUtf8 name)) <&> fmap (\b -> PSObject {value = b, provenance = Attribute (provenance as) name}) +-- | Attribute selector. @a #? #b@ is @a.b@ in Nix, but handles the missing case and the null case without exception. Operates on attributes that are optional (@_?@) and nullable (@Null |.@, @() |.@) in the schema.+(#??) :: (KnownSymbol s, as ? s ~ (Null |. b), PossibleTypesForSchema b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (Maybe (PSObject b))+as #?? p = do+ mv <- as #? p+ join <$> for mv (const (pure Nothing) |! (pure . Just))+ -- | Retrieve an optional attribute but throw if it's missing. -- -- It provides a decent error message with attrset provenance, but can't provide -- extra context like you can when manually handling the @a '#?' b@ 'Nothing' case.-(#?!) :: (KnownSymbol s, OptionalAttrType as s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b)+(#?!) :: (KnownSymbol s, as ? s ~ b, MonadEval m) => PSObject (Attrs' as w) -> AttrLabel s -> m (PSObject b) as #?! p = do as #? p >>= \case Nothing -> throwIO $ MissingAttribute (provenance as) (T.pack (symbolVal p))@@ -333,6 +357,8 @@ NixTypeForSchema NixPath = NixPath NixTypeForSchema Bool = Bool NixTypeForSchema Int64 = Int64+ NixTypeForSchema [a] = NixList+ NixTypeForSchema () = () class PossibleTypesForSchema s where typesForSchema :: Proxy s -> [RawValueType]@@ -351,6 +377,11 @@ instance PossibleTypesForSchema Int64 +instance PossibleTypesForSchema ()++instance PossibleTypesForSchema [a] where+ typesForSchema _ = [getRawValueType (Proxy @NixList)]+ instance (PossibleTypesForSchema a, PossibleTypesForSchema b) => PossibleTypesForSchema (a |. b)@@ -524,3 +555,19 @@ basicAttrsWithProvenance :: Value NixAttrs -> Provenance -> PSObject (Attrs '[]) basicAttrsWithProvenance attrs p = PSObject {value = rtValue attrs, provenance = p}++instance FromPSObject Int64 Int64 where+ fromPSObject o = do+ v <- check o+ liftIO (Expr.fromValue v)++instance forall a b. FromPSObject a b => FromPSObject [a] [b] where+ fromPSObject o = do+ traverseArray fromPSObject o++traverseArray :: (MonadEval m) => (PSObject a -> m b) -> PSObject [a] -> m [b]+traverseArray f o = do+ ov <- check o+ l <- liftIO (Expr.fromValue ov)+ l & zip [0 ..] & traverse \(i, a) ->+ f (PSObject (ListItem (provenance o) i) a :: PSObject a)
src/Hercules/CNix/Expr/Typed.hs view
@@ -19,6 +19,7 @@ match, match', getBool,+ getInt, getStringIgnoreContext, hasContext, CheckType (..),@@ -152,6 +153,10 @@ (0 /=) <$> [C.exp| int { $(Value *v)->boolean ? 1 : 0 }|] +getInt :: Value NixInt -> IO Int64+getInt (Value (RawValue v)) =+ [C.exp| int64_t { $(Value *v)->integer }|]+ -- NOT coerceToString getStringIgnoreContext :: Value NixString -> IO ByteString getStringIgnoreContext (Value (RawValue v)) =@@ -230,3 +235,6 @@ instance HasRawValueType NixList where getRawValueType _ = List++instance HasRawValueType () where+ getRawValueType _ = Null
test/Hercules/CNix/Expr/SchemaSpec.hs view
@@ -8,6 +8,7 @@ import qualified Hercules.CNix.Expr as Expr import Hercules.CNix.Expr.Raw (RawValueType (Attrs, Bool, Lambda, Null, String)) import Hercules.CNix.Expr.Schema+import Hercules.CNix.Expr.Schema (FromPSObject (fromPSObject)) import Protolude hiding (TypeError, check, evalState) import SingleState (evalState) import Test.Hspec@@ -112,7 +113,8 @@ Proxy @( Attrs '[ "optionallyFunction" ::. (NixString ->? NixString),- "optionalAttr" ::? NixString+ "optionalAttr" ::? NixString,+ "nullableAttr" ::?? [StringWithoutContext] ] ) @@ -169,6 +171,37 @@ schema e #? #optionalAttr >>= traverse getByteString_ r `shouldBe` Just "nice"++ describe ".#??" do+ it "can return Nothing for unset" \_ -> do+ r <- runES do+ e <-+ exprWithBasePath+ "{ }"+ "/"+ schema+ e #?? #nullableAttr >>= traverse fromPSObject+ r `shouldBe` (Nothing :: Maybe [ByteString])++ it "can return Nothing for null" \_ -> do+ r <- runES do+ e <-+ exprWithBasePath+ "{ nullableAttr = null; }"+ "/"+ schema+ e #?? #nullableAttr >>= traverse fromPSObject+ r `shouldBe` (Nothing :: Maybe [ByteString])++ it "can return Just" \_ -> do+ r <- runES do+ e <-+ exprWithBasePath+ "{ nullableAttr = [''nice'']; }"+ "/"+ schema+ e #?? #nullableAttr >>= traverse fromPSObject+ r `shouldBe` Just ["nice" :: ByteString] describe "fromPSObject" do describe "@Bool" do