packages feed

pa-field-parser 0.1.0.1 → 0.2.0.0

raw patch · 2 files changed

+90/−5 lines, 2 filesdep +template-haskelldep +timePVP ok

version bump matches the API change (PVP)

Dependencies added: template-haskell, time

API changes (from Hackage documentation)

+ FieldParser: emptyOr :: forall s a. (Eq s, Show s, Monoid s) => FieldParser s a -> FieldParser' Error s (Maybe a)
+ FieldParser: hyphenatedDay :: FieldParser Text Day
+ FieldParser: instance Language.Haskell.TH.Syntax.Lift Data.Fixed.Pico
+ FieldParser: instance Language.Haskell.TH.Syntax.Lift Data.Time.Calendar.Days.Day
+ FieldParser: instance Language.Haskell.TH.Syntax.Lift Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay
+ FieldParser: literal :: forall from to. Lift to => FieldParser from to -> from -> Code Q to
+ FieldParser: notEmptyStringP :: FieldParser Text Text
+ FieldParser: utcTime :: FieldParser Text UTCTime

Files

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.1.0.1+version:            0.2.0.0 synopsis:           “Vertical” parsing of values description:         license:            BSD-3-Clause@@ -74,12 +74,14 @@       base <5,       pa-prelude,       pa-error-tree,-      containers,-      scientific,-      semigroupoids,-      aeson,       aeson-better-errors,+      aeson,       attoparsec,       case-insensitive,+      containers,+      scientific,+      semigroupoids,+      template-haskell,       text,+      time, 
src/FieldParser.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-orphans #-}  module FieldParser where @@ -12,6 +14,7 @@ import Data.Attoparsec.Text qualified as Atto import Data.CaseInsensitive qualified as CaseInsensitive import Data.Error.Tree+import Data.Fixed qualified as Fixed import Data.List.NonEmpty qualified as NonEmpty import Data.Map.Strict qualified as Map import Data.Scientific (Scientific)@@ -19,8 +22,13 @@ import Data.Semigroup.Foldable (Foldable1 (toNonEmpty)) import Data.Semigroupoid qualified as Semigroupoid import Data.Text qualified as Text+import Data.Time qualified as Time+import Data.Time.Format.ISO8601 qualified as Time.Format.ISO+import Language.Haskell.TH qualified as TH+import Language.Haskell.TH.Syntax qualified as TH import PossehlAnalyticsPrelude import Text.ParserCombinators.ReadPrec qualified as Read+import Prelude hiding (or)  -- | Parser for a field. TODO: define what a field is --@@ -145,6 +153,12 @@   Left _err -> Left $ "Not a valid UTF-8 string"   Right a -> Right a +-- | Assert that the string is not empty+notEmptyStringP :: FieldParser Text Text+notEmptyStringP = FieldParser $ \case+  "" -> Left [fmt|String cannot be empty|]+  t -> Right t+ -- | A decimal number with an optional `+` or `-` sign character. signedDecimal :: FieldParser Text Integer signedDecimal =@@ -241,6 +255,34 @@     iMinBound = toInteger (minBound :: i)     iMaxBound = toInteger (maxBound :: i) +-- | ex: @2021-02-23@+hyphenatedDay :: FieldParser Text Time.Day+hyphenatedDay =+  FieldParser $ \t ->+    case parseDay t of+      Nothing -> Left $ [fmt|Not a valid date of format yyyy-mm-dd: "{t}"|]+      Just day -> Right day+  where+    parseDay :: Text -> Maybe Time.Day+    parseDay t =+      t+        & 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)+utcTime :: FieldParser Text Time.UTCTime+utcTime =+  FieldParser $ \t ->+    case parseTime t of+      Nothing -> Left $ [fmt|Not a valid date of format `yyyy-mm-ddThh:mm:ss[.sss]Z` (ISO 8601:2004(E) sec. 4.3.2 extended format): "{t}"|]+      Just day -> Right day+  where+    parseTime :: Text -> Maybe Time.UTCTime+    parseTime t =+      t+        & textToString+        & Time.Format.ISO.iso8601ParseM @Maybe @Time.UTCTime+ -- | Example of how to create a more “complicated” parser that checks whether a value -- is between two other values. clamped ::@@ -323,6 +365,16 @@     flipEither (Left err) = Right err     flipEither (Right a) = Left a +-- | Parse into Nothing if the Monoid (e.g. Text, Map etc.) was empty+emptyOr :: forall s a. (Eq s, Show s, Monoid s) => FieldParser s a -> FieldParser' Error s (Maybe a)+emptyOr inner =+  FieldParser $ \from ->+    if from == mempty+      then Right Nothing+      else case runFieldParser inner from of+        Left err -> Left $ errorContext [fmt|Value was neither empty ("{mempty @s & show}") nor|] err+        Right a -> Right (Just a)+ -- | Given a pretty printing function, it will create a parser -- that uses the inverse function to parse the field. --@@ -472,3 +524,34 @@    in FieldParser $ \bytes -> case parseAll bytes of         Left _attoErr -> Left err         Right a -> Right a++-- | Parse a literal value at compile time. This is used with Template Haskell, like so:+--+-- > $$("2023-07-27" & literal hyphenatedDay) :: Time.Day+--+-- You need the double @$$@!+--+-- 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 parser s = do+  case runFieldParser parser s of+    Right a -> [||a||]+    Left err -> TH.liftCode (err & prettyError & textToString & fail)++{-+NOTE: Lift for library types++Newer versions of Template Haskell provide a `Lift` class,+which lets us parse types from e.g. strings at compile time.++But many types do not implement the stock `Lift` instance yet+(it can be auto-derived by GHC if requested).+-}++deriving stock instance (TH.Lift Fixed.Pico)++deriving stock instance (TH.Lift Time.TimeOfDay)++deriving stock instance (TH.Lift Time.Day)