diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# 0.1.0.0
+
+  * first release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# enum-utf8
+
+An experimental toolkit for rendering enumerated types into `Utf8` from
+the [`render-utf8`](http://hackage.haskell.org/package/render-utf8) package)
+and parsing them back again with the provided `Utf8Parsable` type class.
+It is based on the `render-utf8` and
+[`enum-text`](http://hackage.haskell.org/package/enum-text) packages.
+
+To get the `Buildable` and `TextParsable` instances for an enumerated data type
+use the following pattern:
+
+```
+import Text.Enum.Utf8
+import Text.Utf8
+
+data Foo = FOO_bar | FOO_bar_baz
+  deriving (Bounded,Enum,Eq,Ord,Show)
+
+instance EnumUtf8     Foo
+instance Renderable   Foo where render    = renderEnumUtf8
+instance Utf8Parsable Foo where parseUtf8 = parseEnumUtf8
+
+main :: IO ()
+main = mapM_ (cvtLn . render) [minBound..maxBound::Foo]
+```
+
+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 generating and parsing UTF-8 encoded ByteStrings (suitable for
+cassava), as are `Hashable` functions.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/enum-utf8.cabal b/enum-utf8.cabal
new file mode 100644
--- /dev/null
+++ b/enum-utf8.cabal
@@ -0,0 +1,46 @@
+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: e211f4d5f7164b70e08701690bca40f5199d475d0d41b94e006c0e44404db94f
+
+name:           enum-utf8
+version:        0.1.0.0
+synopsis:       An experimental Utf8 parsing toolkit for enumerated types
+description:    An experimental Utf8 rendering and parsing toolkit for enumerated types based on enum-text and render-utf8. 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.Utf8
+  other-modules:
+      Paths_enum_utf8
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      array
+    , base >=4.8.2 && <10
+    , bytestring
+    , hashable
+    , possibly
+    , render-utf8
+    , unordered-containers
+  default-language: Haskell2010
diff --git a/src/Text/Enum/Utf8.hs b/src/Text/Enum/Utf8.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Enum/Utf8.hs
@@ -0,0 +1,169 @@
+{-# LANGUAGE OverloadedStrings    #-}
+{-# LANGUAGE RecordWildCards      #-}
+{-# LANGUAGE ScopedTypeVariables  #-}
+
+module Text.Enum.Utf8
+  ( EnumUtf8(..)
+  , Utf8Parsable(..)
+  , EnumUtf8Config(..)
+  , defaultEnumUtf8Config
+  ) where
+
+import           Data.Array
+import qualified Data.ByteString.Char8          as B
+import qualified Data.ByteString.Lazy           as BL
+import qualified Data.ByteString.Builder        as BB
+import           Data.Coerce
+import           Data.Hashable
+import           Data.Possibly
+import qualified Data.HashMap.Strict            as HM
+import           Data.String
+import           Text.Utf8
+
+
+-- | a class for 'T.Text' parsers.
+class Utf8Parsable a where
+  parseUtf8 :: Utf8 -> Possibly a
+
+{- | our toolkit for enumerated types which should be defined as follows:
+
+@
+import Text.Enum.Utf8
+import Text.Utf8
+
+data Foo = FOO_bar | FOO_bar_baz
+  deriving (Bounded,Enum,Eq,Ord,Show)
+
+instance EnumUtf8     Foo
+instance Renderable   Foo where render    = renderEnumUtf8
+instance Utf8Parsable Foo where parseUtf8 = parseEnumUtf8
+
+main :: IO ()
+main = mapM_ (cvtLn . render) [minBound..maxBound::Foo]
+@
+
+-}
+class ( Renderable    e
+      , Bounded       e
+      , Enum          e
+      , Eq            e
+      , Ord           e
+      , Show          e
+      , Utf8Parsable  e
+      ) => EnumUtf8 e where
+
+  -- | Configures the textual representation of @e@ generated by renderEnumUtf8.
+  configEnumUtf8 :: e -> EnumUtf8Config
+  configEnumUtf8 _ = defaultEnumUtf8Config
+
+  -- | Generate the standard textual representation according to
+  -- 'configEnumUtf8' by default.
+  renderEnumUtf8 :: e -> Utf8
+  renderEnumUtf8 e = enumUtf8Array ! I e
+
+  -- | Parses an @e@ according to the 'renderEnumUtf8' render.
+  parseEnumUtf8 :: Utf8 -> Possibly e
+  parseEnumUtf8 u = maybe (Left m) Right $ HM.lookup b hashmap_b
+    where
+      m = "parseEnumUtf8: enumeration not recognised: "++show b
+      b = utf8_to_bs u
+
+  -- | A cassava field encoder, using 'the renderEnumUtf8' format.
+  toFieldEnumUtf8 :: e -> B.ByteString
+  toFieldEnumUtf8 e = enumByteStringArray ! I e
+
+  -- | A cassava field parser using the 'renderEnumUtf8' format.
+  fromFieldEnumUtf8_ :: Monad m => B.ByteString -> m e
+  fromFieldEnumUtf8_ bs = maybe (fail msg) return $ HM.lookup bs hashmap_b
+    where
+      msg = "fromFieldEnumUtf8_: enumeration not recognised: "++show bs
+
+  -- | For hashing @e@ with the 'renderEnumUtf8' representation.
+  hashWithSaltEnumUtf8 :: Int -> e -> Int
+  hashWithSaltEnumUtf8 n = hashWithSalt n . toFieldEnumUtf8
+
+
+-------------------------------------------------------------------------------
+-- EnumUtf8Config, defaultEnumUtf8Config
+-------------------------------------------------------------------------------
+
+-- | configures the default implementation of 'renderEnumUtf8'
+data EnumUtf8Config =
+  EnumUtf8Config
+    { _etc_text_prep :: String -> String  -- ^ applied to the output of 'show';
+                                          -- 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'
+    }
+
+defaultEnumUtf8Config :: EnumUtf8Config
+defaultEnumUtf8Config =
+  EnumUtf8Config
+    { _etc_text_prep = defaultTextPrep
+    , _etc_char_prep = defaultCharPrep
+    }
+
+defaultTextPrep :: String -> String
+defaultTextPrep s =
+  case dropWhile (/='_') s of
+    _:rst@(_:_) -> fromString rst
+    _ -> error $ "defaultTextPrep: bad data constructor: "++s
+
+defaultCharPrep :: Char -> Char
+defaultCharPrep c = case c of
+    '_' -> '-'
+    _   -> c
+
+
+-------------------------------------------------------------------------------
+-- arrays
+-------------------------------------------------------------------------------
+
+newtype I a = I { _I :: a }
+  deriving (Eq,Ord)
+
+instance EnumUtf8 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 'configEnumUtf8'
+enumUtf8Array :: forall e . EnumUtf8 e => Array (I e) Utf8
+enumUtf8Array =
+    listArray (I minBound,I maxBound)
+      [ fromString $ map _etc_char_prep $ _etc_text_prep $ show e
+        | e <- [minBound..maxBound :: e]
+        ]
+  where
+    EnumUtf8Config{..} = configEnumUtf8 (minBound :: e)
+
+-- | array of 'B.ByteString' generated from 'renderEnumUtf8'
+enumByteStringArray :: forall e . EnumUtf8 e => Array (I e) B.ByteString
+enumByteStringArray =
+  listArray (I minBound,I maxBound)
+    [ utf8_to_bs $ renderEnumUtf8 e
+      | e <- [minBound..maxBound :: e]
+      ]
+
+
+-------------------------------------------------------------------------------
+-- hashmap
+-------------------------------------------------------------------------------
+
+-- | 'B.ByteString' 'HM.HashMap' based on 'renderEnumUtf8' representation
+hashmap_b :: EnumUtf8 e => HM.HashMap B.ByteString e
+hashmap_b =
+  HM.fromList
+    [ (utf8_to_bs $ renderEnumUtf8 c,c)
+      | c <- [minBound..maxBound]
+      ]
+
+
+-------------------------------------------------------------------------------
+-- utf8_to_bs
+-------------------------------------------------------------------------------
+
+utf8_to_bs :: Utf8 -> B.ByteString
+utf8_to_bs = BL.toStrict . BB.toLazyByteString . coerce
