enum-text (empty) → 0.1.0.0
raw patch · 6 files changed
+279/−0 lines, 6 filesdep +arraydep +basedep +bytestringsetup-changed
Dependencies added: array, base, bytestring, fmt, hashable, possibly, text, unordered-containers
Files
- ChangeLog.md +3/−0
- LICENSE +30/−0
- README.md +32/−0
- Setup.hs +2/−0
- enum-text.cabal +47/−0
- src/Text/Enum/Text.hs +165/−0
+ ChangeLog.md view
@@ -0,0 +1,3 @@+# 0.1.0.0++ * first release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Chris Dornan (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * 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.++ * Neither the name of Chris Dornan nor the names of other+ 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+OWNER 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,32 @@+# enum-text++A simple toolkit for rendering enumerated types into `Text` `Builder` (used by+the [`fmt`](http://hackage.haskell.org/package/fmt) package) and parsing them+back again into Text with the provided `TextParsable` type class.++To get the `Buildable` and `TextParsable` instances for an enumerated data type+use the following pattern:++```+import Fmt+import Text.Enum.Text++data Foo = FOO_bar | FOO_bar_baz+ deriving (Bounded,Enum,Eq,Ord,Show)++instance EnumText Foo+instance Buildable Foo where build = buildEnumText+instance TextParsable Foo where parseText = parseEnumText+```++This will use the default configuration for generating the text of each+enumeration from the derived `show` text, namely:++ * removing the prefix upto and including the first underscore (`_`);+ * converting each subsequent underscore (`_`) into a dash (`-`).++See the Haddocks for details on how to override this default configuration for+any given enumeration type.++Functions for rendering text, generating and parsing UTF-8 encoded ByteStrings+(suitable for cassava) and `Hashable` functions are also provided `EnumText`.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ enum-text.cabal view
@@ -0,0 +1,47 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.1.+--+-- see: https://github.com/sol/hpack+--+-- hash: de41441f44fa188062adf3893567b62b7ee598e3ad3cac9d4a37c598b02684da++name: enum-text+version: 0.1.0.0+synopsis: A text rendering and parsing toolkit for enumerated types+description: A text rendering and parsing toolkit for enumerated types. Please see the README on GitHub at <https://github.com/cdornan/enum-text#readme>+category: Text+homepage: https://github.com/cdornan/enum-text#readme+bug-reports: https://github.com/cdornan/enum-text/issues+author: Chris Dornan+maintainer: chris@chrisdornan.com+copyright: 2019 Chris Dornan+license: BSD3+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ ChangeLog.md++source-repository head+ type: git+ location: https://github.com/cdornan/enum-text++library+ exposed-modules:+ Text.Enum.Text+ other-modules:+ Paths_enum_text+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ array+ , base >=4.8.2 && <10+ , bytestring+ , fmt+ , hashable+ , possibly+ , text+ , unordered-containers+ default-language: Haskell2010
+ src/Text/Enum/Text.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Text.Enum.Text+ ( EnumText(..)+ , TextParsable(..)+ , EnumTextConfig(..)+ , defaultEnumTextConfig+ ) where++import Data.Array+import qualified Data.ByteString.Char8 as B+import Data.Coerce+import Data.Hashable+import Data.Possibly+import qualified Data.HashMap.Strict as HM+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import Fmt+++-- | a class for 'T.Text' parsers.+class TextParsable a where+ parseText :: T.Text -> Possibly a++{- | our toolkit for enumerated types which should be defined as follows:++@+import Fmt+import Text.Enum.Text++data Foo = FOO_bar | FOO_bar_baz+ deriving (Bounded,Enum,Eq,Ord,Show)++instance EnumText Foo+instance Buildable Foo where build = buildEnumText+instance TextParsable Foo where parseText = parseEnumText+@++-}+class ( Buildable e+ , Bounded e+ , Enum e+ , Eq e+ , Ord e+ , Show e+ , TextParsable e+ ) => EnumText e where++ -- | Configures the textual representation of @e@ generated by renderEnumText.+ configEnumText :: e -> EnumTextConfig+ configEnumText _ = defaultEnumTextConfig++ -- | Generate the standard textual representation according to+ -- 'configEnumText' by default.+ renderEnumText :: e -> T.Text+ renderEnumText e = enumTextArray ! I e++ -- | Sames as 'renderEnumText', but generating a 'Builder'.+ buildEnumText :: e -> Builder+ buildEnumText = build . renderEnumText++ -- | Parses an @e@ according to the 'renderEnumText' render.+ parseEnumText :: T.Text -> Possibly e+ parseEnumText txt = maybe (Left msg) Right $ HM.lookup txt hashmap_t+ where+ msg = "parseEnumText: enumeration not recognised: "++show txt++ -- | A cassava field encoder, using 'the renderEnumText' format.+ toFieldEnumText :: e -> B.ByteString+ toFieldEnumText e = enumByteStringArray ! I e++ -- | A cassava field parser using the 'renderEnumText' format.+ fromFieldEnumText_ :: Monad m => B.ByteString -> m e+ fromFieldEnumText_ bs = maybe (fail msg) return $ HM.lookup bs hashmap_b+ where+ msg = "fromFieldEnumText_: enumeration not recognised: "++show bs++ -- | For hashing @e@ with the 'renderEnumText' representation.+ hashWithSaltEnumText :: Int -> e -> Int+ hashWithSaltEnumText n = hashWithSalt n . toFieldEnumText+++-------------------------------------------------------------------------------+-- EnumTextConfig, defaultEnumTextConfig+-------------------------------------------------------------------------------++-- | configures the default implementation of 'renderEnumText'+data EnumTextConfig =+ EnumTextConfig+ { _etc_text_prep :: T.Text -> T.Text -- ^ applied to the output of 'show'+ -- once converted to 'T.Text'; by+ -- default strips each data+ -- constructor up to and including+ -- the first '_'+ , _etc_char_prep :: Char -> Char -- ^ applied to each character of+ -- the outpout of '_etc_text_prep'+ }++defaultEnumTextConfig :: EnumTextConfig+defaultEnumTextConfig =+ EnumTextConfig+ { _etc_text_prep = defaultTextPrep+ , _etc_char_prep = defaultCharPrep+ }++defaultTextPrep :: T.Text -> T.Text+defaultTextPrep txt = case T.uncons $ T.dropWhile (/='_') txt of+ Just (_,rst) | not $ T.null rst -> rst+ _ -> error $ "defaultTextPrep: bad data constructor: "++T.unpack txt++defaultCharPrep :: Char -> Char+defaultCharPrep c = case c of+ '_' -> '-'+ _ -> c+++-------------------------------------------------------------------------------+-- arrays+-------------------------------------------------------------------------------++newtype I a = I { _I :: a }+ deriving (Eq,Ord)++instance EnumText e => Ix (I e) where+ range (l,h) = coerce [_I l.._I h]+ index (l,_) x = fromEnum (_I x) - fromEnum (_I l)+ inRange (l,h) x = _I l <= _I x && _I x <= _I h++-- | array of texts constructed with 'configEnumText'+enumTextArray :: forall e . EnumText e => Array (I e) T.Text+enumTextArray =+ listArray (I minBound,I maxBound)+ [ T.map _etc_char_prep $ _etc_text_prep $ T.pack $ show e+ | e <- [minBound..maxBound :: e]+ ]+ where+ EnumTextConfig{..} = configEnumText (minBound :: e)++-- | array of 'B.ByteString' generated from 'renderEnumText'+enumByteStringArray :: forall e . EnumText e => Array (I e) B.ByteString+enumByteStringArray = listArray (I minBound,I maxBound)+ [ TE.encodeUtf8 $ renderEnumText e+ | e <- [minBound..maxBound :: e]+ ]+++-------------------------------------------------------------------------------+-- hashmaps+-------------------------------------------------------------------------------++-- | 'T.Text' 'HM.HashMap' based on 'renderEnumText' representation+hashmap_t :: EnumText e => HM.HashMap T.Text e+hashmap_t = HM.fromList+ [ (renderEnumText c,c)+ | c <- [minBound..maxBound]+ ]++-- | 'B.ByteString' 'HM.HashMap' based on 'renderEnumText' representation+hashmap_b :: EnumText e => HM.HashMap B.ByteString e+hashmap_b = HM.fromList+ [ (TE.encodeUtf8 $ renderEnumText c,c)+ | c <- [minBound..maxBound]+ ]