packages feed

json-to-haskell 0.0.1.0 → 0.1.0.0

raw patch · 7 files changed

+214/−79 lines, 7 filesdep +ansi-wl-pprintdep +optparse-applicativePVP ok

version bump matches the API change (PVP)

Dependencies added: ansi-wl-pprint, optparse-applicative

API changes (from Hackage documentation)

- JsonToHaskell: [_includeImports] :: Options -> Bool
- JsonToHaskell: [_numberPreference] :: Options -> NumberPreference
- JsonToHaskell: [_stronglyNormalize] :: Options -> Bool
- JsonToHaskell: defaultOptions :: Options
- JsonToHaskell.Internal.Options: DoubleNumbers :: NumberPreference
- JsonToHaskell.Internal.Options: Env :: Options -> Int -> Env
- JsonToHaskell.Internal.Options: FloatNumbers :: NumberPreference
- JsonToHaskell.Internal.Options: Fractional :: NumberType
- JsonToHaskell.Internal.Options: ScientificNumbers :: NumberPreference
- JsonToHaskell.Internal.Options: SmartDoubles :: NumberPreference
- JsonToHaskell.Internal.Options: SmartFloats :: NumberPreference
- JsonToHaskell.Internal.Options: Whole :: NumberType
- JsonToHaskell.Internal.Options: [_includeImports] :: Options -> Bool
- JsonToHaskell.Internal.Options: [_indentationLevel] :: Env -> Int
- JsonToHaskell.Internal.Options: [_numberPreference] :: Options -> NumberPreference
- JsonToHaskell.Internal.Options: [_options] :: Env -> Options
- JsonToHaskell.Internal.Options: [_stronglyNormalize] :: Options -> Bool
- JsonToHaskell.Internal.Options: data Env
- JsonToHaskell.Internal.Options: data NumberPreference
- JsonToHaskell.Internal.Options: defaultOptions :: Options
- JsonToHaskell.Internal.Options: includeImports :: Lens' Options Bool
- JsonToHaskell.Internal.Options: indentationLevel :: Lens' Env Int
- JsonToHaskell.Internal.Options: instance GHC.Classes.Eq JsonToHaskell.Internal.Options.NumberPreference
- JsonToHaskell.Internal.Options: instance GHC.Classes.Ord JsonToHaskell.Internal.Options.NumberType
- JsonToHaskell.Internal.Options: instance GHC.Show.Show JsonToHaskell.Internal.Options.NumberPreference
- JsonToHaskell.Internal.Options: numberPreference :: Lens' Options NumberPreference
- JsonToHaskell.Internal.Options: options :: Lens' Env Options
- JsonToHaskell.Internal.Options: stronglyNormalize :: Lens' Options Bool
- JsonToHaskell.Internal.Printer: buildType :: Struct 'Ref -> Builder ()
+ JsonToHaskell: UseByteString :: TextType
+ JsonToHaskell: UseDoubles :: NumberType
+ JsonToHaskell: UseFloats :: NumberType
+ JsonToHaskell: UseHashMap :: MapType
+ JsonToHaskell: UseList :: ListType
+ JsonToHaskell: UseMap :: MapType
+ JsonToHaskell: UseScientificNumbers :: NumberType
+ JsonToHaskell: UseSmartDoubles :: NumberType
+ JsonToHaskell: UseSmartFloats :: NumberType
+ JsonToHaskell: UseString :: TextType
+ JsonToHaskell: UseText :: TextType
+ JsonToHaskell: UseVector :: ListType
+ JsonToHaskell: [_includeHeader] :: Options -> Bool
+ JsonToHaskell: [_numberType] :: Options -> NumberType
+ JsonToHaskell: data ListType
+ JsonToHaskell: data MapType
+ JsonToHaskell: data NumberType
+ JsonToHaskell: data TextType
+ JsonToHaskell: performantOptions :: Options
+ JsonToHaskell: simpleOptions :: Options
+ JsonToHaskell.Internal.Options: UseDoubles :: NumberType
+ JsonToHaskell.Internal.Options: UseFloats :: NumberType
+ JsonToHaskell.Internal.Options: UseScientificNumbers :: NumberType
+ JsonToHaskell.Internal.Options: UseSmartDoubles :: NumberType
+ JsonToHaskell.Internal.Options: UseSmartFloats :: NumberType
+ JsonToHaskell.Internal.Options: [_includeHeader] :: Options -> Bool
+ JsonToHaskell.Internal.Options: [_numberType] :: Options -> NumberType
+ JsonToHaskell.Internal.Options: includeHeader :: Lens' Options Bool
+ JsonToHaskell.Internal.Options: numberType :: Lens' Options NumberType
+ JsonToHaskell.Internal.Options: simpleOptions :: Options
+ JsonToHaskell.Internal.Parser: Fractional :: NumberVariant
+ JsonToHaskell.Internal.Parser: Whole :: NumberVariant
+ JsonToHaskell.Internal.Parser: data NumberVariant
+ JsonToHaskell.Internal.Parser: instance GHC.Classes.Eq JsonToHaskell.Internal.Parser.NumberVariant
+ JsonToHaskell.Internal.Parser: instance GHC.Classes.Ord JsonToHaskell.Internal.Parser.NumberVariant
+ JsonToHaskell.Internal.Parser: instance GHC.Show.Show JsonToHaskell.Internal.Parser.NumberVariant
+ JsonToHaskell.Internal.Printer: Env :: Options -> Int -> Env
+ JsonToHaskell.Internal.Printer: [_indentationLevel] :: Env -> Int
+ JsonToHaskell.Internal.Printer: [_options] :: Env -> Options
+ JsonToHaskell.Internal.Printer: data Env
+ JsonToHaskell.Internal.Printer: indentationLevel :: Lens' Env Int
+ JsonToHaskell.Internal.Printer: options :: Lens' Env Options
+ JsonToHaskell.Internal.Printer: writeType :: Bool -> Struct 'Ref -> Builder ()
- JsonToHaskell: Options :: Int -> NumberPreference -> TextType -> MapType -> ListType -> Bool -> Bool -> Bool -> Options
+ JsonToHaskell: Options :: Int -> NumberType -> TextType -> MapType -> ListType -> Bool -> Bool -> Options
- JsonToHaskell.Internal.Options: Options :: Int -> NumberPreference -> TextType -> MapType -> ListType -> Bool -> Bool -> Bool -> Options
+ JsonToHaskell.Internal.Options: Options :: Int -> NumberType -> TextType -> MapType -> ListType -> Bool -> Bool -> Options
- JsonToHaskell.Internal.Parser: [SNumber] :: NumberType -> Struct r
+ JsonToHaskell.Internal.Parser: [SNumber] :: NumberVariant -> Struct r

Files

+ app/Flags.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE QuasiQuotes #-}+module Flags where++import Options.Applicative+import JsonToHaskell+import Text.RawString.QQ (r)+import Text.PrettyPrint.ANSI.Leijen++optionsParserInfo :: ParserInfo Options+optionsParserInfo = info (optionParser <**> helper) fullDesc++parseNumberType :: ReadM NumberType+parseNumberType = maybeReader $ \case+  "smart-floats" -> Just UseSmartFloats+  "smart-doubles" -> Just UseSmartDoubles+  "floats" -> Just UseFloats+  "doubles" -> Just UseDoubles+  _ -> Nothing++parseTextType :: ReadM TextType+parseTextType = maybeReader $ \case+  "string" -> Just UseString+  "text" -> Just UseText+  "bytestring" -> Just UseByteString+  _ -> Nothing++parseMapType :: ReadM MapType+parseMapType = maybeReader $ \case+  "map" -> Just UseMap+  "hashmap" -> Just UseHashMap+  _ -> Nothing++parseListType :: ReadM ListType+parseListType = maybeReader $ \case+  "list" -> Just UseList+  "vector" -> Just UseVector+  _ -> Nothing++stringDoc :: String -> Mod f a+stringDoc = helpDoc . Just . string++optionParser :: Parser Options+optionParser = do+    _tabStop <- option auto+                (short 't'+                 <> long "tab-stop"+                 <> stringDoc "Number of spaces to indent each level."+                 <> value 2)+    _numberType <- option parseNumberType+                (short 'n'+                 <> long "numbers"+                 <> value UseSmartDoubles+                 <> stringDoc [r|Type to use for numbers.++'smart-floats':+    Use floats for numbers with decimals, Int for whole numbers.+'smart-doubles':+    Use floats for numbers with decimals, Int for whole numbers.+'floats':+    Use floats for all numbers.+'doubles':+    Use doubles for all numbers.+'scientific':+    Use scientific for all numbers.+|])+    _textType <- option parseTextType+                (short 's'+                 <> long "strings"+                 <> value UseText+                 <> stringDoc [r|Type to use for strings.++'string':+  Use String for strings.+'text':+  Use Text for strings.+'bytestring':+  Use ByteString for strings.+|])+    _mapType <- option parseMapType+                (short 'm'+                 <> long "maps"+                 <> value UseMap+                 <> stringDoc [r|Type to use for maps.++'map':+  Use Data.Map for maps.+'hashmap':+  Use Data.HashMap for maps.+|])+    _listType <- option parseListType+                (short 'l'+                 <> long "lists"+                 <> value UseList+                 <> stringDoc [r|Type to use for lists.++'list':+  Use [] for lists.+'vector':+  Use Data.Vector for lists.+|])+    _includeHeader <- flag True False+                (long "no-module-header"+                 <> stringDoc [r|Omit the module header containing language extensions, module definition and imports.|])+    _strictData <- flag False True+                (long "strict"+                 <> stringDoc [r|Use strict record fields.|])+    pure $ Options {.. }
app/Main.hs view
@@ -4,39 +4,17 @@ module Main where  import JsonToHaskell-import Data.Aeson hiding (defaultOptions)-import Text.RawString.QQ (r)-import Data.Text.IO as T-import Data.ByteString.Lazy as BS--value :: Either String Value-value = eitherDecode valueStr--valueStr :: BS.ByteString-valueStr = [r|-{-  "name": "jon",-  "age and stuff": 37,-  "is-employed": true,-  "\"pets_maybe": [["Garfield"], ["Odie"]],-  "address": {-    "street": "221B",-    "zip": 12345,-    "other" : {-      "one": 1-    }-  },-  "other-address": {-    "street": "221B",-    "zip2": 12345,-    "other" : {-      "two": [{}]-    }-  }-}-|]+import qualified Data.Text.IO as T+import qualified Data.ByteString.Lazy as BL+import Options.Applicative+import System.Exit+import Flags+import Data.Aeson  main :: IO () main = do-    v <- either fail pure value-    T.putStrLn $ jsonToHaskell defaultOptions v+    opts <- execParser optionsParserInfo+    input <- BL.getContents+    case eitherDecode input of+        Left err -> putStrLn err >> exitWith (ExitFailure 1)+        Right val -> T.putStrLn $ jsonToHaskell opts val
json-to-haskell.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 1079a1406cdee3bc2efef1e996c7a1567e6e39d4c155e7edbaafb79a86a52880+-- hash: ccb186e695b31c1b3ebeb957e8b52dc2a25db22eac621ea58f87a819385927f5  name:           json-to-haskell-version:        0.0.1.0+version:        0.1.0.0 description:    Please see the README on GitHub at <https://github.com/githubuser/json-to-haskell#readme> homepage:       https://github.com/githubuser/json-to-haskell#readme bug-reports:    https://github.com/githubuser/json-to-haskell/issues@@ -55,6 +55,7 @@ executable json-to-haskell-exe   main-is: Main.hs   other-modules:+      Flags       Paths_json_to_haskell   hs-source-dirs:       app@@ -62,6 +63,7 @@   build-depends:       aeson     , aeson-extra+    , ansi-wl-pprint     , base >=4.7 && <5     , bimap     , bytestring@@ -71,6 +73,7 @@     , microlens-platform     , mtl     , nonempty-containers+    , optparse-applicative     , raw-strings-qq     , recursion-schemes     , text
src/JsonToHaskell.hs view
@@ -11,8 +11,13 @@ {-# LANGUAGE TypeApplications #-} module JsonToHaskell     ( jsonToHaskell+    , simpleOptions+    , performantOptions     , Options(..)-    , defaultOptions+    , NumberType(..)+    , TextType(..)+    , MapType(..)+    , ListType(..)     ) where  import JsonToHaskell.Internal.Options@@ -22,6 +27,10 @@ import qualified Data.Text as T import qualified Data.Bimap as BM +-- | Transform an Aeson 'Value' into a complete Haskell module (as 'T.Text').+-- This function is all that you need.+--+-- Use 'defaultOptions' as a reasonable starting point. jsonToHaskell :: Options -> Value -> T.Text jsonToHaskell opts v = do     let allStructs = analyze v
src/JsonToHaskell/Internal/Options.hs view
@@ -3,76 +3,87 @@  import Lens.Micro.Platform (makeLenses) -data NumberPreference =-    SmartFloats-  | SmartDoubles-  | FloatNumbers-  | DoubleNumbers-  | ScientificNumbers+-- | Choose which type to use for Numbers+data NumberType =+    -- | Use 'Int' for whole numbers, 'Float' for decimals+    UseSmartFloats+    -- | Use 'Int' for whole numbers, 'Double' for decimals+  | UseSmartDoubles+    -- | Use 'Float' for all numbers+  | UseFloats+    -- | Use 'Double' for all numbers+  | UseDoubles+    -- | Use 'Scientific' for all numbers+  | UseScientificNumbers   deriving (Show, Eq) +-- | Choose which type to use for strings data TextType =+    -- | Use 'String' for strings     UseString+    -- | Use 'Text' for string   | UseText+    -- | Use 'ByteString' for strings   | UseByteString   deriving (Show, Eq) +-- | Choose which type to use for key-value maps data MapType =+    -- | Use Data.Map     UseMap+    -- | Use Data.HashMap   | UseHashMap   deriving (Show, Eq) +-- | Choose which type to use for arrays data ListType =+    -- | Use lists     UseList+    -- | Use vectors   | UseVector   deriving (Show, Eq) +-- | Options for module generation data Options = Options   { _tabStop :: Int-  , _numberPreference :: NumberPreference+  , _numberType :: NumberType   , _textType :: TextType   , _mapType :: MapType   , _listType :: ListType-  , _includeImports :: Bool-  , _stronglyNormalize :: Bool+  , _includeHeader :: Bool+  -- , _stronglyNormalize :: Bool   , _strictData :: Bool   }  -data Env = Env-    { _options :: Options-    , _indentationLevel  :: Int-    }- makeLenses ''Options-makeLenses ''Env -defaultOptions :: Options-defaultOptions = Options+-- | Simple module generation options.+-- These are reasonable defaults for a simple module+simpleOptions :: Options+simpleOptions = Options     { _tabStop = 2-    , _numberPreference = DoubleNumbers+    , _numberType = UseDoubles     , _textType = UseText     , _mapType = UseMap     , _listType = UseList-    , _includeImports = False-    , _stronglyNormalize = True+    , _includeHeader = False+    -- , _stronglyNormalize = True     , _strictData = False     } +-- | Use more performant data types, use these for production apps. performantOptions :: Options performantOptions = Options     { _tabStop = 2-    , _numberPreference = DoubleNumbers+    , _numberType = UseDoubles     , _textType = UseText     , _mapType = UseMap     , _listType = UseList-    , _includeImports = False-    , _stronglyNormalize = True+    , _includeHeader = False     -- TODO+    -- , _stronglyNormalize = True     , _strictData = True     } --data NumberType = Fractional | Whole-  deriving (Show, Eq, Ord) 
src/JsonToHaskell/Internal/Parser.hs view
@@ -8,7 +8,6 @@ {-# LANGUAGE OverloadedStrings #-} module JsonToHaskell.Internal.Parser where -import JsonToHaskell.Internal.Options import Control.Monad.State import Control.Monad.Reader import Data.Aeson (Value)@@ -27,6 +26,10 @@ import qualified Data.Vector as V import qualified Data.Set as S +-- | Used to track whether the value was fractional or whole.+data NumberVariant = Fractional | Whole+  deriving (Show, Eq, Ord)+ -- a DataKind for tracking whether a structure contains nested structs or Record Names data RecordType = Ref | Structure -- | The representation of a record's field types@@ -38,7 +41,7 @@         SRecordRef :: T.Text -> Struct 'Ref         SMap :: Struct r -> Struct r         SBool :: Struct r-        SNumber :: NumberType -> Struct r+        SNumber :: NumberVariant -> Struct r         SNull :: Struct r         SString :: Struct r         SValue :: Struct r
src/JsonToHaskell/Internal/Printer.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-}@@ -6,6 +7,7 @@ {-# LANGUAGE GADTs #-} module JsonToHaskell.Internal.Printer where +import Lens.Micro.Platform (makeLenses) import JsonToHaskell.Internal.Parser import JsonToHaskell.Internal.Options import Control.Monad.State@@ -21,6 +23,15 @@ import Lens.Micro.Platform (view, (+~), (<&>))  +-- | The environment used for printing the module+data Env = Env+    { _options :: Options+    , _indentationLevel  :: Int+    }+makeLenses ''Env+++ -- | Convert a name into a valid haskell field name toFieldName :: T.Text -> T.Text toFieldName = T.filter (isAlphaNum) . T.pack . toCamel . fromAny . T.unpack . T.dropWhile (not . isAlpha)@@ -66,7 +77,9 @@                         else tell ", "             tell $ toFieldName k             tell " :: "-            buildType v+            useStrictData <- view (options . strictData)+            when useStrictData (tell "!")+            writeType False v     indented . line $ do         tell "} "         tell "deriving (Show, Eq, Ord)"@@ -113,29 +126,37 @@   -- | Write out the Haskell representation for a given JSON type-buildType :: Struct 'Ref -> Builder ()-buildType =-  \case-    SNull -> tell "()"+writeType :: Bool -> Struct 'Ref -> Builder ()+writeType nested struct = do+  strict <- view (options . strictData)+  let wrapOuter = if strict || nested then parens else id+  case struct of+    SNull -> wrapOuter $ tell "Maybe Value"     SString -> do         getTextType >>= tell     SNumber t -> do-        pref <- view (options . numberPreference)+        pref <- view (options . numberType)         case (pref, t) of-            (FloatNumbers, _) -> tell "Float"-            (DoubleNumbers, _) -> tell "Double"-            (ScientificNumbers, _) -> tell "Scientific"-            (SmartFloats, Fractional) -> tell "Float"-            (SmartFloats, Whole) -> tell "Int"-            (SmartDoubles, Fractional) -> tell "Double"-            (SmartDoubles, Whole) -> tell "Int"+            (UseFloats, _) -> tell "Float"+            (UseDoubles, _) -> tell "Double"+            (UseScientificNumbers, _) -> tell "Scientific"+            (UseSmartFloats, Fractional) -> tell "Float"+            (UseSmartFloats, Whole) -> tell "Int"+            (UseSmartDoubles, Fractional) -> tell "Double"+            (UseSmartDoubles, Whole) -> tell "Int"      SBool -> tell "Bool"     SValue -> tell "Value"     SMap s -> do         txtType <- getTextType-        tell ("Map " <> txtType <> " ") >> parens (buildType s)-    SArray s -> tell "Vector " >> parens (buildType s)+        mapStr <- view (options . mapType) <&> \case+            UseMap -> "Map"+            UseHashMap -> "HashMap"+        wrapOuter $ tell (mapStr <> " " <> txtType <> " ") >> writeType True s+    SArray s -> do+        view (options . listType) >>= \case+          UseList -> tell "[" >> writeType True s >> tell "]"+          UseVector -> wrapOuter $ tell "Vector " >> writeType True s     SRecordRef n -> tell n   where     getTextType = do