packages feed

text-encode (empty) → 0.1.0.0

raw patch · 13 files changed

+1060/−0 lines, 13 filesdep +aesondep +basedep +bytestring

Dependencies added: aeson, base, bytestring, casing, cassava, http-api-data, http-types, persistent, postgresql-simple, sqlite-simple, text, text-encode

Files

+ LICENSE view
@@ -0,0 +1,26 @@+Copyright 2025 Daniel Brice++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1.  Redistributions of source code must retain the above copyright notice, this+    list of conditions and the following disclaimer.++2.  Redistributions in binary form must reproduce the above copyright notice,+    this list of conditions and the following disclaimer in the documentation+    and/or other materials provided with the distribution.++3.  Neither the name of the copyright holder nor the names of its contributors+    may be used to endorse or promote products derived from this software+    without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,17 @@+# text-encode++Classes and newtypes for deriving uniform textual encodings.++This repository hosts seven packages.+The core package is `text-encode`.+Subsidiary packages are provided in order to avoid unnecessary dependencies in downstream packages.+The subsidiary packages re-export the modules from the core package, so there is no need to import the core package if you are importing one or more of the subsidiary packages.++- `text-encode-aeson`+- `text-encode-cassava`+- `text-encode-http-api-data`+- `text-encode-persistent`+- `text-encode-postgresql-simple`+- `text-encode-sqlite-simple`++See the module documentation in `Text.Encode` for usage.
+ src/dev-main/Main.hs view
@@ -0,0 +1,2 @@+module Main (main) where+import DevMain (main)
+ src/dev/DevMain.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE OverloadedStrings #-}++module DevMain (main) where++import Text.Encode+import Text.Encode.Aeson ()+import Text.Encode.Cassava ()+import Text.Encode.Persistent ()++import Data.Aeson qualified as Aeson+import Data.ByteString.Lazy (LazyByteString)+import Data.Csv qualified as Cassava+import Data.Functor (($>))+import Database.Persist qualified as Persistent++data MyType = MyTypeFoo | MyTypeBar | MyTypeFooBar+    deriving stock (Read, Show, Eq, Ord, Bounded, Enum)+  deriving TextEncode+    via DeriveTextEncode (Cased 'Pascal 'QuietSnake (DropPrefix "MyType" ReadShowEncode)) MyType+  deriving+    ( Aeson.FromJSON, Aeson.ToJSON+    , Cassava.FromField, Cassava.ToField+    , Persistent.PersistField+    )+    via ViaTextEncode MyType++data YourType = YourTypeFoo | YourTypeBar | YourTypeFooBar+  deriving stock (Read, Show, Eq, Ord, Bounded, Enum)+  deriving TextEncode+    via DeriveTextEncode BoundedEnumEncode YourType+  deriving+    ( Aeson.FromJSON, Aeson.ToJSON+    , Cassava.FromField, Cassava.ToField+    , Persistent.PersistField+    )+    via ViaTextEncode YourType++myCases :: [(MyType, LazyByteString)]+myCases =+  [ (MyTypeFoo, "foo")+  , (MyTypeBar, "bar")+  , (MyTypeFooBar, "foo_bar")+  ]++yourCases :: [(YourType, LazyByteString)]+yourCases =+  [ (YourTypeFoo, "0")+  , (YourTypeBar, "1")+  , (YourTypeFooBar, "2")+  ]++quoted :: LazyByteString -> LazyByteString+quoted = ("\"" <>) . (<> "\"")++checkCase :: (Eq a, Show a, Aeson.FromJSON a, Aeson.ToJSON a) => (a, LazyByteString) -> IO Int+checkCase (dec, enc) = do+  let enc' = quoted enc+  putStr $ "Checking:\t" <> show dec <> " <-> " <> show enc+  let encodingWorks = Aeson.encode dec == enc'+  let decodingWorks = Aeson.decode enc' == Just dec+  if encodingWorks && decodingWorks+    then putStrLn "\tPassed." $> 0+    else putStrLn "\tFAILED!" $> 1++main :: IO ()+main = do+  myFailures <- traverse checkCase myCases+  yourFailures <- traverse checkCase yourCases+  let failures = sum (myFailures <> yourFailures)+  if failures == 0+    then putStrLn "All tests passed."+    else putStrLn $ "There were " <> show failures <> " failures."
+ src/text-encode-aeson/Text/Encode/Aeson.hs view
@@ -0,0 +1,60 @@+module Text.Encode.Aeson (+  module Text.Encode,+  JsonEncode,+) where++import Text.Encode++import Data.Aeson (+  FromJSON (..),+  FromJSONKey (..),+  ToJSON (..),+  ToJSONKey (..),+  Value (..),+  eitherDecode,+  encode+ )+import Data.ByteString.Lazy.Char8 qualified as LC8+import Data.Coerce (coerce)+import Data.Text.Lazy qualified as LT+import Data.Text.Lazy.Encoding qualified as LTE+import Data.Typeable (Typeable)++instance (TextEncode a, Typeable a) => FromJSON (ViaTextEncode a) where+  parseJSON (String txt) = either fail pure $ decodeText txt+  parseJSON raw = fail $ typedError @a "parseJSON" "Expected String, got " $ encode raw++instance TextEncode a => ToJSON (ViaTextEncode a) where+  toJSON = String . encodeText+  toEncoding = toEncoding . encodeText++  {-# INLINE toJSON #-}+  {-# INLINE toEncoding #-}++instance (TextEncode a, Typeable a) => FromJSONKey (ViaTextEncode a)+instance TextEncode a => ToJSONKey (ViaTextEncode a)++data JsonEncode++instance (FromJSON a, ToJSON a) => TextEncode (DeriveTextEncode JsonEncode a) where+  encodeLazyByteString = coerce $ encode @a+  decodeLazyByteString = coerce $ eitherDecode @a+  encodeByteString = LC8.toStrict . encodeLazyByteString+  decodeByteString = decodeLazyByteString . LC8.fromStrict+  encodeString = LC8.unpack <$> encodeLazyByteString+  decodeString = decodeLazyByteString . LC8.pack+  encodeLazyText = LTE.decodeLatin1 . encodeLazyByteString+  decodeLazyText = decodeLazyByteString . LTE.encodeUtf8+  encodeText = LT.toStrict . encodeLazyText+  decodeText = decodeLazyText . LT.fromStrict++  {-# INLINE encodeLazyByteString #-}+  {-# INLINE decodeLazyByteString #-}+  {-# INLINE encodeByteString #-}+  {-# INLINE decodeByteString #-}+  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}+  {-# INLINE encodeLazyText #-}+  {-# INLINE decodeLazyText #-}+  {-# INLINE encodeText #-}+  {-# INLINE decodeText #-}
+ src/text-encode-cassava/Text/Encode/Cassava.hs view
@@ -0,0 +1,39 @@+module Text.Encode.Cassava (+  module Text.Encode,+  CsvEncode,+) where++import Text.Encode++import Data.ByteString.Char8 qualified as C8+import Data.Coerce (coerce)+import Data.Csv (FromField (..), ToField (..), runParser)+import Data.Text.Encoding qualified as T++instance TextEncode a => FromField (ViaTextEncode a) where+  parseField = either fail pure . coerce (decodeByteString @a)++  {-# INLINE parseField #-}++instance TextEncode a => ToField (ViaTextEncode a) where+  toField = coerce $ encodeByteString @a++  {-# INLINE toField #-}++data CsvEncode++instance (FromField a, ToField a) => TextEncode (DeriveTextEncode CsvEncode a) where+  encodeByteString = coerce $ toField @a+  decodeByteString = coerce $ runParser . parseField @a++  encodeText = T.decodeLatin1 . encodeByteString+  decodeText = decodeByteString . T.encodeUtf8+  encodeString = C8.unpack . encodeByteString+  decodeString = decodeByteString . C8.pack++  {-# INLINE encodeByteString #-}+  {-# INLINE decodeByteString #-}+  {-# INLINE encodeText #-}+  {-# INLINE decodeText #-}+  {-# INLINE  encodeString #-}+  {-# INLINE  decodeString #-}
+ src/text-encode-http-api-data/Text/Encode/HttpApiData.hs view
@@ -0,0 +1,59 @@+module Text.Encode.HttpApiData (+  module Text.Encode,+  UrlEncode,+) where++import Text.Encode++import Data.Bifunctor (first)+import Data.Coerce (coerce)+import Data.Text qualified as T+import Data.Text.Encoding qualified as TE+import Web.FormUrlEncoded (FromFormKey (..), ToFormKey (..))+import Web.HttpApiData (FromHttpApiData (..), ToHttpApiData (..))++instance TextEncode a => ToFormKey (ViaTextEncode a) where+  toFormKey = coerce $ encodeText @a++  {-# INLINE toFormKey #-}++instance TextEncode a => FromFormKey (ViaTextEncode a) where+  parseFormKey = coerce $ first T.pack . decodeText @a++  {-# INLINE parseFormKey #-}++instance TextEncode a => ToHttpApiData (ViaTextEncode a) where+  toUrlPiece = coerce $ encodeText @a+  toHeader = TE.encodeUtf8 . toUrlPiece+  toQueryParam = toUrlPiece++  {-# INLINE toUrlPiece #-}+  {-# INLINE toHeader #-}+  {-# INLINE toQueryParam #-}++instance TextEncode a => FromHttpApiData (ViaTextEncode a) where+  parseUrlPiece = coerce $ first T.pack . decodeText @a+  parseHeader = parseUrlPiece . TE.decodeLatin1+  parseQueryParam = parseUrlPiece++  {-# INLINE parseUrlPiece #-}+  {-# INLINE parseHeader #-}+  {-# INLINE parseQueryParam #-}++data UrlEncode++instance (FromHttpApiData a, ToHttpApiData a) => TextEncode (DeriveTextEncode UrlEncode a) where+  encodeText = coerce $ toUrlPiece @a+  decodeText = coerce $ first T.unpack . parseUrlPiece @a++  encodeString = T.unpack . encodeText+  decodeString = decodeText . T.pack+  encodeByteString = TE.encodeUtf8 . encodeText+  decodeByteString = decodeText . TE.decodeLatin1++  {-# INLINE encodeText #-}+  {-# INLINE decodeText #-}+  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}+  {-# INLINE encodeByteString #-}+  {-# INLINE decodeByteString #-}
+ src/text-encode-persistent/Text/Encode/Persistent.hs view
@@ -0,0 +1,42 @@+module Text.Encode.Persistent (+  module Text.Encode,+  PersistentEncode,+) where++import Text.Encode++import Data.Bifunctor+import Data.Coerce+import Data.Text qualified as T+import Data.Text.Encoding qualified as TE+import Data.Typeable+import Database.Persist.Class+import Database.Persist.PersistValue++instance (TextEncode a, Typeable a) => PersistField (ViaTextEncode a) where+  {-# INLINE toPersistValue #-}+  toPersistValue = coerce $ PersistText . encodeText @(ViaTextEncode a)++  fromPersistValue (PersistText bs) = coerce $ first T.pack $ decodeText @(ViaTextEncode a) bs+  fromPersistValue x = Left $ T.pack $ "DeriveTextEncode PersistentEncode requires PersistText: " <> show x++data PersistentEncode++instance PersistField a => TextEncode (DeriveTextEncode PersistentEncode a) where+  encodeText (DeriveTextEncode x) =+    case toPersistValue x of+      (PersistText txt) -> txt+      x' -> error $ "DeriveTextEncode PersistentEncode requires PersistText: " <> show x'++  decodeText = bimap T.unpack DeriveTextEncode . fromPersistValue . PersistText++  encodeByteString = TE.encodeUtf8 . encodeText+  decodeByteString = decodeText . TE.decodeLatin1+  encodeString = T.unpack . encodeText+  decodeString = decodeText . T.pack++  {-# INLINE decodeText #-}+  {-# INLINE encodeByteString #-}+  {-# INLINE decodeByteString #-}+  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}
+ src/text-encode-postgresql-simple/Text/Encode/PostgresqlSimple.hs view
@@ -0,0 +1,32 @@+module Text.Encode.PostgresqlSimple (+  module Text.Encode,+  TextEncodePostgresqlSimpleError,+) where++import Text.Encode++import Control.Exception (Exception)+import Control.Monad ((<=<))+import Data.Coerce (coerce)+import Data.Text qualified as T+import Data.Typeable (Typeable)+import Database.PostgreSQL.Simple.FromField (FromField (..), conversionError)+import Database.PostgreSQL.Simple.ToField (ToField (..))++newtype TextEncodePostgresqlSimpleError = TextEncodePostgresqlSimpleError String+  deriving (Show)++instance Exception TextEncodePostgresqlSimpleError++instance (TextEncode a, Typeable a) => FromField (ViaTextEncode a) where+  fromField f =+    either (conversionError . TextEncodePostgresqlSimpleError) (pure . ViaTextEncode)+      . decodeText @a+      <=< fromField @T.Text f++  {-# INLINE fromField #-}++instance (TextEncode a, Typeable a) => ToField (ViaTextEncode a) where+  toField = coerce $ toField . encodeText @a++  {-# INLINE toField #-}
+ src/text-encode-sqlite-simple/Text/Encode/SqliteSimple.hs view
@@ -0,0 +1,33 @@+module Text.Encode.SqliteSimple (+  module Text.Encode,+  TextEncodeSqliteSimpleError,+) where++import Text.Encode++import Control.Exception (Exception, toException)+import Control.Monad ((<=<))+import Data.Coerce (coerce)+import Data.Text qualified as T+import Data.Typeable (Typeable)+import Database.SQLite.Simple.FromField (FromField (..))+import Database.SQLite.Simple.Ok (Ok (..))+import Database.SQLite.Simple.ToField (ToField (..))++newtype TextEncodeSqliteSimpleError = TextEncodeSqliteSimpleError String+  deriving (Show)++instance Exception TextEncodeSqliteSimpleError++instance (TextEncode a, Typeable a) => FromField (ViaTextEncode a) where+  fromField =+    either (Errors . pure . toException . TextEncodeSqliteSimpleError) (pure . ViaTextEncode)+      . decodeText @a+      <=< fromField @T.Text++  {-# INLINE fromField #-}++instance (TextEncode a, Typeable a) => ToField (ViaTextEncode a) where+  toField = coerce $ toField . encodeText @a++  {-# INLINE toField #-}
+ src/text-encode/Text/Encode.hs view
@@ -0,0 +1,190 @@+-- | This module provides classes and newtypes for deriving uniform textual encodings.+--+-- An instance @'TextEncode' A@ defines a uniform textual representation for data of type @A@.+-- Writing instances is usually straightforward, or they may be derived using 'DeriveTextEncode'.+-- Use 'ViaTextEncode' to derive instances of other classes based on a type's 'TextEncode' instance.+--+-- For example,+-- @+--     import Text.Encode+--+--     import Text.Encode.Aeson ()+--     import Text.Encode.Cassava ()+--     import Text.Encode.Persistent ()+--+--     data MyType = MyTypeFoo | MyTypeBar | MyTypeFooBar+--       deriving stock (Read, Show)+--       deriving 'TextEncode'+--         via 'DeriveTextEncode' ('Cased' 'Pascal' 'QuietSnake' ('DropPrefix' "MyType" 'ReadShowEncode')) MyType+--       deriving+--         ( Aeson.FromJSON, Aeson.ToJSON+--         , Cassava.FromField, Cassava.ToField+--         , Persistent.PersistField+--         )+--         via 'ViaTextEncode' MyType+-- @+--+-- This will derive a 'TextEncode' instance for @MyType@ based on the stock 'Read' and 'Show' instances,+-- modified by the 'Cased' and 'DropPrefix' options. Uniform, mutually-consistent instances for Aeson,+-- Cassava, and Persistent classes are then derived from the derived 'TextEncode' instance.+module Text.Encode (+  -- * Textual Encodings+  TextEncode (..),+  -- * Deriving 'TextEncode'+  DeriveTextEncode (..),+  -- ** Base encodings+  BoundedEnumEncode,+  ReadShowEncode,+  -- ** Add or remove prefixes+  DropPrefix,+  AddPrefix,+  -- ** Transform casing+  Casing (..),+  CaseConversion (..),+  Cased,+  -- * Deriving other classes+  ViaTextEncode (..),+  -- * Utilities+  typedError,+  decodeError,+  maybeDecode,+) where++import Text.Encode.Casing++import Control.Monad (guard, (<=<))+import Data.ByteString.Char8 qualified as C8+import Data.ByteString.Lazy.Char8 qualified as LC8+import Data.Coerce (coerce)+import Data.Kind (Type)+import Data.Text qualified as T+import Data.Text.Lazy qualified as LT+import Data.Typeable (Typeable, Proxy (..), typeRep)+import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)+import Text.Read (readMaybe)++-- | An instance @'TextEncode' a@ defines a uniform textual representation for data of type @a@.+--+-- Instances are assumed to follow these mutual-coherence properties:+-- * @decodeString . encodeString === Right@+-- * @fmap encodeString . decodeString === Right@+-- * @decodeText . "Data.Text".'pack' . encodeString === Right@+-- * @fmap encodeString . decodeByteString === Right . "Data.ByteString.Char8".'unpack'@+-- and permutations thereof.+--+-- Mutually-coherent default definitions of the methods are provided, though+-- users may supply their own implementations for performance reasons.+-- It is the user's responsibility to ensure that these definitions mutually cohere.+class TextEncode a where+  {-# MINIMAL encodeString, decodeString #-}+  encodeString :: a -> String+  decodeString :: String -> Either String a+  encodeByteString :: a -> C8.ByteString+  decodeByteString :: C8.ByteString -> Either String a+  encodeLazyByteString :: a -> LC8.ByteString+  decodeLazyByteString :: LC8.ByteString -> Either String a+  encodeText :: a -> T.Text+  decodeText :: T.Text -> Either String a+  encodeLazyText :: a -> LT.Text+  decodeLazyText :: LT.Text -> Either String a++  encodeText = T.pack . encodeString+  decodeText = decodeString . T.unpack+  encodeLazyText = LT.fromStrict . encodeText+  decodeLazyText = decodeText . LT.toStrict+  encodeByteString = C8.pack . encodeString+  decodeByteString = decodeString . C8.unpack+  encodeLazyByteString = LC8.fromStrict . encodeByteString+  decodeLazyByteString = decodeByteString . LC8.toStrict++  {-# INLINE encodeByteString #-}+  {-# INLINE decodeByteString #-}+  {-# INLINE encodeLazyByteString #-}+  {-# INLINE decodeLazyByteString #-}+  {-# INLINE encodeText #-}+  {-# INLINE decodeText #-}+  {-# INLINE encodeLazyText #-}+  {-# INLINE decodeLazyText #-}++-- | Derive instances of various classes based on an instance of 'TextEncode'.+newtype ViaTextEncode a = ViaTextEncode a+  deriving TextEncode via a++instance TextEncode a => Show (ViaTextEncode a) where+  show (ViaTextEncode a) = encodeString a++instance TextEncode a => Read (ViaTextEncode a) where+  readsPrec _ = either (const []) (pure . (, "") . ViaTextEncode) . decodeString++newtype DeriveTextEncode (opt :: Type) a = DeriveTextEncode a+  deriving (Eq, Ord, Read, Show, Bounded, Enum) via a++data BoundedEnumEncode++instance (Bounded a, Enum a, Typeable a) => TextEncode (DeriveTextEncode BoundedEnumEncode a) where+  encodeString = show . fromEnum+  decodeString = coerce $ maybeDecode @a $ toEnumMaybe <=< readMaybe++  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}++data ReadShowEncode++instance (Read a, Show a, Typeable a) => TextEncode (DeriveTextEncode ReadShowEncode a) where+  encodeString = show+  decodeString = coerce $ maybeDecode @a readMaybe++  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}++data DropPrefix (pfx :: Symbol) (opt :: Type)++instance (KnownSymbol pfx, TextEncode (DeriveTextEncode opt a)) => TextEncode (DeriveTextEncode (DropPrefix pfx opt) a) where+  encodeString = coerce $ dropPfx @pfx . encodeString @(DeriveTextEncode opt a)+  decodeString = coerce $ decodeString @(DeriveTextEncode opt a) . (symbolVal (Proxy @pfx) <>)++  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}++data AddPrefix (pfx :: Symbol) (opt :: Type)++instance (KnownSymbol pfx, TextEncode (DeriveTextEncode opt a)) => TextEncode (DeriveTextEncode (AddPrefix pfx opt) a) where+  encodeString = coerce $ (symbolVal (Proxy @pfx) <>) . encodeString @(DeriveTextEncode opt a)+  decodeString = coerce $ decodeString @(DeriveTextEncode opt a) . dropPfx @pfx++  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}++data Cased (decoding :: Casing) (encoding :: Casing) (opt :: Type)++instance (CaseConversion d e, TextEncode (DeriveTextEncode opt a)) => TextEncode (DeriveTextEncode (Cased d e opt) a) where+  encodeString = coerce $ encoding @d @e . encodeString @(DeriveTextEncode opt a)+  decodeString = coerce $ decodeString @(DeriveTextEncode opt a) . decoding @d @e++  {-# INLINE encodeString #-}+  {-# INLINE decodeString #-}++type FunctionName = String+type Message = String+type Input a = a++typedError :: forall a b. (Typeable a, Show b) => FunctionName -> Message -> Input b -> String+typedError fn msg input = mconcat [fn, " @", show (typeRep $ Proxy @a), ": ", msg, show input]++decodeError :: forall a b. Typeable a => Input String -> Either String b+decodeError input = Left $ typedError @a "decode" "Failed to decode " input++maybeDecode :: forall a. Typeable a => (String -> Maybe a) -> Input String -> Either String a+maybeDecode f raw = maybe (decodeError @a raw) Right $ f raw++toEnumMaybe :: forall a. (Bounded a, Enum a) => Int -> Maybe a+toEnumMaybe n = toEnum n <$ guard (fromEnum (minBound @a) <= n && n <= fromEnum (maxBound @a))++dropPfx :: forall pfx. KnownSymbol pfx => String -> String+dropPfx = drop $ length $ symbolVal $ Proxy @pfx++{-# INLINE typedError #-}+{-# INLINE decodeError #-}+{-# INLINE maybeDecode #-}+{-# INLINE toEnumMaybe #-}+{-# INLINE dropPfx #-}
+ src/text-encode/Text/Encode/Casing.hs view
@@ -0,0 +1,270 @@+module Text.Encode.Casing (+  Casing(..),+  CaseConversion(..),+) where++import Text.Casing qualified as C++data Casing+  = Camel+  | Kebab+  | Pascal+  | QuietSnake+  | ScreamingSnake+  | Snake++class CaseConversion (decoding :: Casing) (encoding :: Casing) where+  encoding :: String -> String+  decoding :: String -> String++instance CaseConversion 'Camel 'Camel where+  encoding = id+  decoding = id++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Camel 'Kebab where+  encoding = C.toKebab . C.fromHumps+  decoding = C.toCamel . C.fromKebab++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Camel 'Pascal where+  encoding = C.toPascal . C.fromHumps+  decoding = C.toCamel . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Camel 'QuietSnake where+  encoding = C.toQuietSnake . C.fromHumps+  decoding = C.toCamel . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Camel 'ScreamingSnake where+  encoding = C.toScreamingSnake . C.fromHumps+  decoding = C.toCamel . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Camel 'Snake where+  encoding = C.toSnake . C.fromHumps+  decoding = C.toCamel . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'Camel where+  encoding = C.toCamel . C.fromKebab+  decoding = C.toKebab . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'Kebab where+  encoding = id+  decoding = id++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'Pascal where+  encoding = C.toPascal . C.fromKebab+  decoding = C.toKebab . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'QuietSnake where+  encoding = C.toQuietSnake . C.fromKebab+  decoding = C.toKebab . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'ScreamingSnake where+  encoding = C.toScreamingSnake . C.fromKebab+  decoding = C.toKebab . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Kebab 'Snake where+  encoding = C.toSnake . C.fromKebab+  decoding = C.toKebab . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'Camel where+  encoding = C.toCamel . C.fromHumps+  decoding = C.toPascal . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'Kebab where+  encoding = C.toKebab . C.fromHumps+  decoding = C.toPascal . C.fromKebab++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'Pascal where+  encoding = id+  decoding = id++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'QuietSnake where+  encoding = C.toQuietSnake . C.fromHumps+  decoding = C.toPascal . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'ScreamingSnake where+  encoding = C.toScreamingSnake . C.fromHumps+  decoding = C.toPascal . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Pascal 'Snake where+  encoding = C.toSnake . C.fromHumps+  decoding = C.toPascal . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'Camel where+  encoding = C.toCamel . C.fromSnake+  decoding = C.toQuietSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'Kebab where+  encoding = C.toKebab . C.fromSnake+  decoding = C.toQuietSnake . C.fromKebab++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'Pascal where+  encoding = C.toPascal . C.fromSnake+  decoding = C.toQuietSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'QuietSnake where+  encoding = id+  decoding = id++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'ScreamingSnake where+  encoding = C.toScreamingSnake . C.fromSnake+  decoding = C.toQuietSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'QuietSnake 'Snake where+  encoding = C.toSnake . C.fromSnake+  decoding = C.toQuietSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'Camel where+  encoding = C.toCamel . C.fromSnake+  decoding = C.toScreamingSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'Kebab where+  encoding = C.toKebab . C.fromSnake+  decoding = C.toScreamingSnake . C.fromKebab++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'Pascal where+  encoding = C.toPascal . C.fromSnake+  decoding = C.toScreamingSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'QuietSnake where+  encoding = C.toQuietSnake . C.fromSnake+  decoding = C.toScreamingSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'ScreamingSnake where+  encoding = id+  decoding = id++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'ScreamingSnake 'Snake where+  encoding = C.toSnake . C.fromSnake+  decoding = C.toScreamingSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'Camel where+  encoding = C.toCamel . C.fromSnake+  decoding = C.toSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'Kebab where+  encoding = C.toKebab . C.fromSnake+  decoding = C.toSnake . C.fromKebab++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'Pascal where+  encoding = C.toPascal . C.fromSnake+  decoding = C.toSnake . C.fromHumps++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'QuietSnake where+  encoding = C.toQuietSnake . C.fromSnake+  decoding = C.toSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'ScreamingSnake where+  encoding = C.toScreamingSnake . C.fromSnake+  decoding = C.toSnake . C.fromSnake++  {-# INLINE encoding #-}+  {-# INLINE decoding #-}++instance CaseConversion 'Snake 'Snake where+  encoding = id+  decoding = id++  {-# INLINE decoding #-}+  {-# INLINE encoding #-}
+ text-encode.cabal view
@@ -0,0 +1,218 @@+cabal-version:      3.0+name:               text-encode+version:            0.1.0.0+homepage:           https://github.com/friedbrice/text-encode#readme+bug-reports:        https://github.com/friedbrice/text-encode/issues+author:             Daniel Brice+maintainer:         danielbrice@gmail.com+copyright:          Copyright Daniel Brice+license:            BSD-3-Clause+license-file:       LICENSE+build-type:         Simple+category:           Serialization+synopsis:+  Classes and newtypes for deriving uniform textual encodings.++description:+  Classes and newtypes for deriving uniform textual encodings.+  See the Text.Encode module documentation for details and usage examples.++extra-source-files: README.md+tested-with:+  GHC ==9.2.8 || ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.2++source-repository head+  type:     git+  location: https://github.com/friedbrice/text-encode++common dialect+  ghc-options:        -Wall+  build-depends:      base >=4.16 && <5+  default-language:   Haskell2010+  default-extensions:+    AllowAmbiguousTypes+    DataKinds+    DerivingVia+    FlexibleContexts+    FlexibleInstances+    ImportQualifiedPost+    KindSignatures+    MultiParamTypeClasses+    ScopedTypeVariables+    TupleSections+    TypeApplications+    UndecidableInstances++common opts-dist+  ghc-options: -O2++common opts-dev+  ghc-options:        -O0 -Wwarn+  default-extensions:+    InstanceSigs+    PartialTypeSignatures++common deps-lib+  build-depends:+    , bytestring  >=0.11 && <0.13+    , casing      >=0.1  && <0.2+    , text        >=1.2  && <2.2++common deps-aeson+  build-depends:+    , aeson       >=2.0  && <2.3+    , bytestring  >=0.11 && <0.13+    , text        >=1.2  && <2.2++common deps-cassava+  build-depends:+    , bytestring  >=0.11 && <0.13+    , cassava     >=0.5  && <0.6+    , text        >=1.2  && <2.2++common deps-http-api-data+  build-depends:+    , bytestring     >=0.11 && <0.13+    , http-api-data  >=0.4  && <0.7+    , http-types     >=0.12 && <0.13+    , text           >=1.2  && <2.2++common deps-persistent+  build-depends:+    , persistent  >=2.13 && <2.18+    , text        >=1.2  && <2.2++common deps-postgresql-simple+  build-depends:+    , postgresql-simple  >=0.6 && <0.8+    , text               >=1.2 && <2.2++common deps-sqlite-simple+  build-depends:+    , sqlite-simple  >=0.4 && <0.5+    , text           >=1.2 && <2.2++common deps-dev++library+  import:          dialect+  import:          opts-dist+  import:          deps-lib+  hs-source-dirs:  src/text-encode++  -- cabal-fmt: expand src/text-encode+  exposed-modules:+    Text.Encode+    Text.Encode.Casing++library text-encode-aeson+  import:          dialect+  import:          opts-dist+  import:          deps-aeson+  visibility:      public+  ghc-options:     -Wno-orphans+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-aeson++  -- cabal-fmt: expand src/text-encode-aeson+  exposed-modules: Text.Encode.Aeson++library text-encode-cassava+  import:          dialect+  import:          opts-dist+  import:          deps-cassava+  visibility:      public+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-cassava++  -- cabal-fmt: expand src/text-encode-cassava+  exposed-modules: Text.Encode.Cassava++library text-encode-http-api-data+  import:          dialect+  import:          opts-dist+  import:          deps-http-api-data+  visibility:      public+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-http-api-data++  -- cabal-fmt: expand src/text-encode-http-api-data+  exposed-modules: Text.Encode.HttpApiData++library text-encode-persistent+  import:          dialect+  import:          opts-dist+  import:          deps-persistent+  visibility:      public+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-persistent++  -- cabal-fmt: expand src/text-encode-persistent+  exposed-modules: Text.Encode.Persistent++library text-encode-postgresql-simple+  import:          dialect+  import:          opts-dist+  import:          deps-postgresql-simple+  visibility:      public+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-postgresql-simple++  -- cabal-fmt: expand src/text-encode-postgresql-simple+  exposed-modules: Text.Encode.PostgresqlSimple++library text-encode-sqlite-simple+  import:          dialect+  import:          opts-dist+  import:          deps-sqlite-simple+  visibility:      public+  ghc-options:     -Wno-orphans+  build-depends:   text-encode+  hs-source-dirs:  src/text-encode-sqlite-simple++  -- cabal-fmt: expand src/text-encode-sqlite-simple+  exposed-modules: Text.Encode.SqliteSimple++test-suite dev+  import:         dialect+  import:         opts-dev+  import:         deps-lib+  import:         deps-aeson+  import:         deps-cassava+  import:         deps-http-api-data+  import:         deps-persistent+  import:         deps-postgresql-simple+  import:         deps-sqlite-simple+  import:         deps-dev+  type:           exitcode-stdio-1.0+  ghc-options:    -Wno-orphans+  main-is:        Main.hs+  hs-source-dirs:+    src/text-encode src/text-encode-aeson src/text-encode-cassava+    src/text-encode-http-api-data src/text-encode-persistent+    src/text-encode-postgresql-simple src/text-encode-sqlite-simple+    src/dev src/dev-main++  -- cabal-fmt: expand src/text-encode+  -- cabal-fmt: expand src/text-encode-aeson+  -- cabal-fmt: expand src/text-encode-cassava+  -- cabal-fmt: expand src/text-encode-http-api-data+  -- cabal-fmt: expand src/text-encode-persistent+  -- cabal-fmt: expand src/text-encode-postgresql-simple+  -- cabal-fmt: expand src/text-encode-sqlite-simple+  -- cabal-fmt: expand src/dev+  other-modules:+    DevMain+    Text.Encode+    Text.Encode.Aeson+    Text.Encode.Casing+    Text.Encode.Cassava+    Text.Encode.HttpApiData+    Text.Encode.Persistent+    Text.Encode.PostgresqlSimple+    Text.Encode.SqliteSimple