pa-field-parser 0.2.0.1 → 0.3.0.0
raw patch · 3 files changed
+44/−14 lines, 3 filesdep ~pa-preludePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: pa-prelude
API changes (from Hackage documentation)
- FieldParser: jsonParser :: Monad m => FieldParser' err Value to -> ParseT err m to
+ FieldParser: toJsonParser :: Monad m => FieldParser' err Value to -> ParseT err m to
+ FieldParser: utcTimeLenient :: FieldParser Text UTCTime
Files
- CHANGELOG.md +5/−0
- pa-field-parser.cabal +2/−2
- src/FieldParser.hs +37/−12
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for pa-field-parser +## 0.3.0.0 -- 2023-10-15++- rename `jsonParser` to `toJsonParser`+- add `utcTimeLenient`+ ## 0.2.0.0 -- 2023-07-28 - Add a few parsers
pa-field-parser.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 -- !!! ATTN: file autogenerated from pa-template.cabal.mustache on toplevel !!! name: pa-field-parser-version: 0.2.0.1+version: 0.3.0.0 synopsis: “Vertical” parsing of values description: license: BSD-3-Clause@@ -72,7 +72,7 @@ hs-source-dirs: src build-depends: base <5,- pa-prelude,+ pa-prelude >= 0.2.0.0, pa-error-tree, aeson-better-errors, aeson,
src/FieldParser.hs view
@@ -17,7 +17,6 @@ import Data.Fixed qualified as Fixed import Data.List.NonEmpty qualified as NonEmpty import Data.Map.Strict qualified as Map-import Data.Scientific (Scientific) import Data.Scientific qualified as Scientific import Data.Semigroup.Foldable (Foldable1 (toNonEmpty)) import Data.Semigroupoid qualified as Semigroupoid@@ -83,7 +82,7 @@ toParseJSON parser = Json.toAesonParser prettyError- (jsonParser parser)+ (toJsonParser parser) -- | Turn a @FieldParser' ErrorTree Value@ directly into a valid 'parseJSON` implementation. --@@ -95,7 +94,7 @@ toParseJSONErrorTree parser = Json.toAesonParser prettyErrorTree- (jsonParser parser)+ (toJsonParser parser) toReadPrec :: -- | ReadPrec to base this parser on (e.g. use @readPrec \@Text@@ to parse the same as Text)@@ -109,8 +108,8 @@ Right a -> pure a -- | Turn a @FieldParser Value@ into an 'Json.ParseT` which parses this value.-jsonParser :: Monad m => FieldParser' err Json.Value to -> Json.ParseT err m to-jsonParser parser =+toJsonParser :: (Monad m) => FieldParser' err Json.Value to -> Json.ParseT err m to+toJsonParser parser = ( Json.asValue >>= ( \from -> runFieldParser parser from & \case@@ -191,12 +190,12 @@ Cat.>>> integralToNatural @Integer -- | Parse any integral into a natural number, fails if the integral is negative.-integralToNatural :: Integral i => FieldParser i Natural+integralToNatural :: (Integral i) => FieldParser i Natural integralToNatural = FieldParser (\i -> i & intToNatural & annotate [fmt|Number must be 0 or positive, but was negative: {toInteger i}|]) -- | Parse any integral to an 'Integer'. This can never fail, but is here to mirror 'integralToNatural'.-integralToInteger :: Integral i => FieldParser' err i Integer+integralToInteger :: (Integral i) => FieldParser' err i Integer integralToInteger = lmap (fromIntegral @_ @Integer) Cat.id -- | An arbitrary-precision number in scientific notation.@@ -227,7 +226,7 @@ -- * don’t wrap around the bound -- * don’t fill up all our memory -- * Fit into the available floating point representation space-boundedScientificRealFloat :: RealFloat d => FieldParser Scientific d+boundedScientificRealFloat :: (RealFloat d) => FieldParser Scientific d boundedScientificRealFloat = FieldParser $ \s -> Scientific.toBoundedRealFloat s & first@@ -269,7 +268,8 @@ & textToString & Time.Format.ISO.iso8601ParseM @Maybe @Time.Day --- | @yyyy-mm-ddThh:mm:ss[.sss]Z@ (ISO 8601:2004(E) sec. 4.3.2 extended format)+-- | Parse a timestamp into a UTC time, accepting only UTC timestamps like+-- @yyyy-mm-ddThh:mm:ss[.sss]Z@ (ISO 8601:2004(E) sec. 4.3.2 extended format) utcTime :: FieldParser Text Time.UTCTime utcTime = FieldParser $ \t ->@@ -283,6 +283,31 @@ & textToString & Time.Format.ISO.iso8601ParseM @Maybe @Time.UTCTime +-- | Parse a timestamp into a UTC time, accepting multiple timezone formats.+-- Do not use this if you can force the input to use the `Z` UTC notation (e.g. in a CSV), use 'utcTime' instead.+--+-- Accepts+--+-- * UTC timestamps: @yyyy-mm-ddThh:mm:ss[.sss]Z@+-- * timestamps with time zone: @yyyy-mm-ddThh:mm:ss[.sss]±hh:mm@+--+-- ( both ISO 8601:2004(E) sec. 4.3.2 extended format)+--+-- The time zone of the second kind of timestamp is taken into account, but normalized to UTC (it’s not preserved what the original time zone was)+utcTimeLenient :: FieldParser Text Time.UTCTime+utcTimeLenient =+ FieldParser $ \t ->+ case (t & parseTime @Time.UTCTime)+ <|> (t & parseTime @Time.ZonedTime <&> Time.zonedTimeToUTC) of+ Nothing -> Left $ [fmt|Not a valid date of format `yyyy-mm-ddThh:mm:ss[.sss]Z` or `@yyyy-mm-ddThh:mm:ss[.sss]±hh:mm@` (ISO 8601:2004(E) sec. 4.3.2 extended format): "{t}"|]+ Just day -> Right day+ where+ parseTime :: (Time.Format.ISO.ISO8601 t) => Text -> Maybe t+ parseTime t =+ t+ & textToString+ & Time.Format.ISO.iso8601ParseM+ -- | Example of how to create a more “complicated” parser that checks whether a value -- is between two other values. clamped ::@@ -307,7 +332,7 @@ -- If you want to match on an 'Enum'-like type, -- you should probably use 'invertPretty' or 'invertPrettyCaseInsensitive' instead, -- which allows for exhaustiveness checks.-oneOf :: Ord from => (from -> Text) -> [(from, to)] -> FieldParser from to+oneOf :: (Ord from) => (from -> Text) -> [(from, to)] -> FieldParser from to oneOf errDisplay m = -- This doesn’t strictly need an 'Ord' instance, it can also use `findMaybe` with `==` instead of going through a map. oneOfMap errDisplay (Map.fromList m)@@ -398,7 +423,7 @@ & lmap CaseInsensitive.mk -- | 'oneOf' but only one value possible-exactly :: Eq from => (from -> Text) -> from -> FieldParser from from+exactly :: (Eq from) => (from -> Text) -> from -> FieldParser from from exactly errDisplay from = FieldParser $ \from' -> if from == from' then Right from'@@ -534,7 +559,7 @@ -- ATTN: This needs an instance of the 'TH.Lift' class for the output type. -- Many library types don’t yet implement this class, so we have to provide the instances ourselves. -- See NOTE: Lift for library types-literal :: forall from to. TH.Lift to => FieldParser from to -> from -> TH.Code TH.Q to+literal :: forall from to. (TH.Lift to) => FieldParser from to -> from -> TH.Code TH.Q to literal parser s = do case runFieldParser parser s of Right a -> [||a||]