language-avro 0.1.0.0 → 0.1.1.0
raw patch · 5 files changed
+306/−233 lines, 5 filesdep +containersdep +directorydep +hspec-megaparsecdep ~avroPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: containers, directory, hspec-megaparsec
Dependency ranges changed: avro
API changes (from Hackage documentation)
- Language.Avro.Types: type Schema = Type
+ Language.Avro.Parser: parseDecimal :: MonadParsec Char Text m => m Decimal
+ Language.Avro.Types: Array :: Schema -> Schema
+ Language.Avro.Types: Boolean :: Schema
+ Language.Avro.Types: Bytes :: Maybe LogicalTypeBytes -> Schema
+ Language.Avro.Types: Double :: Schema
+ Language.Avro.Types: Enum :: TypeName -> [TypeName] -> Maybe Text -> Vector Text -> Schema
+ Language.Avro.Types: Fixed :: TypeName -> [TypeName] -> Int -> Maybe LogicalTypeFixed -> Schema
+ Language.Avro.Types: Float :: Schema
+ Language.Avro.Types: Int :: Maybe LogicalTypeInt -> Schema
+ Language.Avro.Types: Long :: Maybe LogicalTypeLong -> Schema
+ Language.Avro.Types: Map :: Schema -> Schema
+ Language.Avro.Types: NamedType :: TypeName -> Schema
+ Language.Avro.Types: Null :: Schema
+ Language.Avro.Types: Record :: TypeName -> [TypeName] -> Maybe Text -> Maybe Order -> [Field] -> Schema
+ Language.Avro.Types: String :: Maybe LogicalTypeString -> Schema
+ Language.Avro.Types: Union :: Vector Schema -> Schema
+ Language.Avro.Types: [aliases] :: Schema -> [TypeName]
+ Language.Avro.Types: [doc] :: Schema -> Maybe Text
+ Language.Avro.Types: [fields] :: Schema -> [Field]
+ Language.Avro.Types: [item] :: Schema -> Schema
+ Language.Avro.Types: [logicalTypeB] :: Schema -> Maybe LogicalTypeBytes
+ Language.Avro.Types: [logicalTypeF] :: Schema -> Maybe LogicalTypeFixed
+ Language.Avro.Types: [logicalTypeI] :: Schema -> Maybe LogicalTypeInt
+ Language.Avro.Types: [logicalTypeL] :: Schema -> Maybe LogicalTypeLong
+ Language.Avro.Types: [logicalTypeS] :: Schema -> Maybe LogicalTypeString
+ Language.Avro.Types: [name] :: Schema -> TypeName
+ Language.Avro.Types: [options] :: Schema -> Vector Schema
+ Language.Avro.Types: [order] :: Schema -> Maybe Order
+ Language.Avro.Types: [size] :: Schema -> Int
+ Language.Avro.Types: [symbols] :: Schema -> Vector Text
+ Language.Avro.Types: [values] :: Schema -> Schema
+ Language.Avro.Types: data Schema
+ Language.Avro.Types: instance GHC.Classes.Ord Language.Avro.Types.Argument
+ Language.Avro.Types: instance GHC.Classes.Ord Language.Avro.Types.ImportType
+ Language.Avro.Types: instance GHC.Classes.Ord Language.Avro.Types.Method
+ Language.Avro.Types: instance GHC.Classes.Ord Language.Avro.Types.Namespace
+ Language.Avro.Types: instance GHC.Classes.Ord Language.Avro.Types.Protocol
+ Language.Avro.Types: pattern Bytes' :: Schema
+ Language.Avro.Types: pattern Int' :: Schema
+ Language.Avro.Types: pattern Long' :: Schema
+ Language.Avro.Types: pattern String' :: Schema
- Language.Avro.Types: Protocol :: Maybe Namespace -> Text -> [ImportType] -> [Schema] -> [Method] -> Protocol
+ Language.Avro.Types: Protocol :: Maybe Namespace -> Text -> Set ImportType -> Set Schema -> Set Method -> Protocol
- Language.Avro.Types: [imports] :: Protocol -> [ImportType]
+ Language.Avro.Types: [imports] :: Protocol -> Set ImportType
- Language.Avro.Types: [messages] :: Protocol -> [Method]
+ Language.Avro.Types: [messages] :: Protocol -> Set Method
- Language.Avro.Types: [types] :: Protocol -> [Schema]
+ Language.Avro.Types: [types] :: Protocol -> Set Schema
Files
- README.md +130/−101
- language-avro.cabal +6/−3
- src/Language/Avro/Parser.hs +74/−36
- src/Language/Avro/Types.hs +15/−11
- test/Spec.hs +81/−82
README.md view
@@ -2,6 +2,8 @@ [](https://github.com/kutyel/avro-parser-haskell/actions) [](https://hackage.haskell.org/package/language-avro)+[](http://stackage.org/nightly/package/language-avro)+[](http://stackage.org/lts/package/language-avro) [](https://github.com/tweag/ormolu) Language definition and parser for AVRO (`.avdl`) files.@@ -9,119 +11,146 @@ ## Example ```haskell-#! /usr/bin/env runhaskell+#!/usr/bin/env stack+-- stack --resolver lts-15.0 script --package language-avro,megaparsec,pretty-simple module Main where import Language.Avro.Parser (readWithImports)+import Text.Megaparsec.Error (ShowErrorComponent (..), errorBundlePretty)+import Text.Pretty.Simple (pPrint) +instance ShowErrorComponent Char where+ showErrorComponent = show+ main :: IO ()-main = readWithImports "test" "PeopleService.avdl"+main =+ readWithImports "test" "PeopleService.avdl"+ >>= either (putStrLn . errorBundlePretty) pPrint -- λ>--- Right--- (Protocol {--- ns = Just (Namespace ["example","seed","server","protocol","avro"]),--- pname = "PeopleService", imports = [IdlImport "People.avdl"],--- types = [--- Record {--- name = "Person",--- aliases = [],--- doc = Nothing,--- order = Nothing,--- fields = [--- Field {--- fldName = "name",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = String,--- fldDefault = Nothing--- },--- Field {--- fldName = "age",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = Int,--- fldDefault = Nothing+-- Protocol+-- { ns = Just+-- ( Namespace+-- [ "example"+-- , "seed"+-- , "server"+-- , "protocol"+-- , "avro"+-- ]+-- )+-- , pname = "PeopleService"+-- , imports = [ IdlImport "People.avdl" ]+-- , types =+-- [ Record+-- { name = "Person"+-- , aliases = []+-- , doc = Nothing+-- , order = Nothing+-- , fields =+-- [ Field+-- { fldName = "name"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = String { logicalTypeS = Nothing }+-- , fldDefault = Nothing+-- }+-- , Field+-- { fldName = "age"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = Int { logicalTypeI = Nothing }+-- , fldDefault = Nothing+-- }+-- ] -- }--- ]--- },--- Record {--- name = "NotFoundError",--- aliases = [],--- doc = Nothing,--- order = Nothing,--- fields = [--- Field {--- fldName = "message",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = String,--- fldDefault = Nothing+-- , Record+-- { name = "NotFoundError"+-- , aliases = []+-- , doc = Nothing+-- , order = Nothing+-- , fields =+-- [ Field+-- { fldName = "message"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = String { logicalTypeS = Nothing }+-- , fldDefault = Nothing+-- }+-- ] -- }--- ]--- },--- Record {--- name = "DuplicatedPersonError",--- aliases = [],--- doc = Nothing,--- order = Nothing,--- fields = [--- Field {--- fldName = "message",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = String,--- fldDefault = Nothing+-- , Record+-- { name = "DuplicatedPersonError"+-- , aliases = []+-- , doc = Nothing+-- , order = Nothing+-- , fields =+-- [ Field+-- { fldName = "message"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = String { logicalTypeS = Nothing }+-- , fldDefault = Nothing+-- }+-- ] -- }--- ]--- },--- Record {--- name = "PeopleRequest",--- aliases = [],--- doc = Nothing,--- order = Nothing,--- fields = [--- Field {--- fldName = "name",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = String,--- fldDefault = Nothing+-- , Record+-- { name = "PeopleRequest"+-- , aliases = []+-- , doc = Nothing+-- , order = Nothing+-- , fields =+-- [ Field+-- { fldName = "name"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = String { logicalTypeS = Nothing }+-- , fldDefault = Nothing+-- }+-- ] -- }--- ]--- },--- Record {--- name = "PeopleResponse",--- aliases = [],--- doc = Nothing,--- order = Nothing,--- fields = [--- Field {--- fldName = "result",--- fldAliases = [],--- fldDoc = Nothing,--- fldOrder = Nothing,--- fldType = Union {options = [NamedType "Person",NamedType "NotFoundError",NamedType "DuplicatedPersonError"]},--- fldDefault = Nothing+-- , Record+-- { name = "PeopleResponse"+-- , aliases = []+-- , doc = Nothing+-- , order = Nothing+-- , fields =+-- [ Field+-- { fldName = "result"+-- , fldAliases = []+-- , fldDoc = Nothing+-- , fldOrder = Nothing+-- , fldType = Union+-- { options =+-- [ NamedType "Person"+-- , NamedType "NotFoundError"+-- , NamedType "DuplicatedPersonError"+-- ]+-- }+-- , fldDefault = Nothing+-- }+-- ] -- }--- ]--- }--- ],--- messages = [--- Method {--- mname = "getPerson",--- args = [Argument {atype = NamedType "example.seed.server.protocol.avro.PeopleRequest", aname = "request"}],--- result = NamedType "example.seed.server.protocol.avro.PeopleResponse",--- throws = Null,--- oneway = False--- }--- ]--- })+-- ]+-- , messages =+-- [ Method+-- { mname = "getPerson"+-- , args =+-- [ Argument+-- { atype = NamedType "example.seed.server.protocol.avro.PeopleRequest"+-- , aname = "request"+-- }+-- ]+-- , result = NamedType "example.seed.server.protocol.avro.PeopleResponse"+-- , throws = Null+-- , oneway = False+-- }+-- ]+-- } ``` ⚠️ Warning: `readWithImports` only works right now if the import type is `"idl"`!
language-avro.cabal view
@@ -1,5 +1,5 @@ name: language-avro-version: 0.1.0.0+version: 0.1.1.0 synopsis: Language definition and parser for AVRO files. description: Parser for the AVRO language specification, see README.md for more details. homepage: https://github.com/kutyel/avro-parser-haskell#readme@@ -17,7 +17,9 @@ exposed-modules: Language.Avro.Types, Language.Avro.Parser build-depends: base >=4.12 && <5- , avro+ , avro >=0.4.7+ , containers+ , directory , filepath , megaparsec , text@@ -37,9 +39,10 @@ -with-rtsopts=-N build-depends: base >=4.12 && <5- , avro+ , avro >=0.4.7 , language-avro , megaparsec , text , hspec+ , hspec-megaparsec , vector
src/Language/Avro/Parser.hs view
@@ -3,32 +3,37 @@ {-# LANGUAGE OverloadedStrings #-} -- | Parser for AVRO (@.avdl@) files, as defined in <http://avro.apache.org/docs/1.8.2/spec.html>.-module Language.Avro.Parser (- -- * Main parsers- parseProtocol- , readWithImports- -- * Intermediate parsers- , parseAliases- , parseAnnotation- , parseImport- , parseMethod- , parseNamespace- , parseOrder- , parseSchema- ) where+module Language.Avro.Parser+ ( -- * Main parsers+ parseProtocol,+ readWithImports, + -- * Intermediate parsers+ parseAliases,+ parseAnnotation,+ parseDecimal,+ parseImport,+ parseMethod,+ parseNamespace,+ parseOrder,+ parseSchema,+ )+where++import Control.Monad (filterM) import Data.Avro import Data.Avro.Schema import Data.Either (partitionEithers)-import Data.List (foldl')+import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.IO as T-import Data.Vector (Vector, fromList)+import qualified Data.Vector as V import Language.Avro.Types+import System.Directory (doesFileExist)+import System.FilePath import Text.Megaparsec import Text.Megaparsec.Char import qualified Text.Megaparsec.Char.Lexer as L-import System.FilePath spaces :: MonadParsec Char T.Text m => m () spaces = L.space space1 (L.skipLineComment "//") (L.skipBlockComment "/*" "*/")@@ -121,9 +126,9 @@ Protocol ns name- [i | ProtocolThingImport i <- things]- [t | ProtocolThingType t <- things]- [m | ProtocolThingMethod m <- things]+ (S.fromList [i | ProtocolThingImport i <- things])+ (S.fromList [t | ProtocolThingType t <- things])+ (S.fromList [m | ProtocolThingMethod m <- things]) data ProtocolThing = ProtocolThingImport ImportType@@ -133,11 +138,11 @@ serviceThing :: MonadParsec Char T.Text m => m ProtocolThing serviceThing = try (ProtocolThingImport <$> parseImport)- <|> try (ProtocolThingMethod <$> parseMethod)- <|> ProtocolThingType <$> parseSchema+ <|> try (ProtocolThingMethod <$> parseMethod)+ <|> ProtocolThingType <$> parseSchema <* optional (symbol ";") -parseVector :: MonadParsec Char T.Text m => m a -> m (Vector a)-parseVector t = fromList <$> braces (lexeme $ sepBy1 t $ symbol ",")+parseVector :: MonadParsec Char T.Text m => m a -> m (V.Vector a)+parseVector t = V.fromList <$> braces (lexeme $ sepBy1 t $ symbol ",") parseTypeName :: MonadParsec Char T.Text m => m TypeName parseTypeName = toNamedType . pure <$> identifier@@ -159,12 +164,12 @@ parseField :: MonadParsec Char T.Text m => m Field parseField =- (\o t a n -> Field n a Nothing o t Nothing) -- FIXME: docs and default values are not supported yet.+ (\o t a n -> Field n a Nothing o t Nothing) -- FIXME: docs are not supported yet. <$> optional parseOrder <*> parseSchema <*> option [] parseFieldAlias <*> identifier- <* symbol ";"+ <* (symbol ";" <|> symbol "=" <* manyTill anySingle (symbol ";")) -- FIXME: default values are not supported yet. -- | Parses arguments of methods into the 'Argument' structure. parseArgument :: MonadParsec Char T.Text m => m Argument@@ -181,17 +186,28 @@ <*> option False (True <$ reserved "oneway") <* symbol ";" +-- | Parses the special type @decimal@ into it's corresponding 'Decimal' structure.+parseDecimal :: MonadParsec Char T.Text m => m Decimal+parseDecimal = toDec <$ reserved "decimal" <*> parens (lexeme $ sepBy1 number $ symbol ",")+ where+ toDec :: [Int] -> Decimal+ toDec [precision] = Decimal (fromIntegral precision) 0+ toDec [precision, scale] = Decimal (fromIntegral precision) (fromIntegral scale)+ toDec _ = error "decimal types can only be specified using two numbers!"+ -- | Parses a single type respecting @Data.Avro.Schema@'s 'Schema'. parseSchema :: MonadParsec Char T.Text m => m Schema parseSchema = Null <$ (reserved "null" <|> reserved "void") <|> Boolean <$ reserved "boolean"- <|> Int <$ reserved "int"- <|> Long <$ reserved "long"+ <|> Int' <$ reserved "int"+ <|> Long' <$ reserved "long"+ <|> Long . Just . DecimalL <$> parseDecimal <|> Float <$ reserved "float" <|> Double <$ reserved "double"- <|> Bytes <$ reserved "bytes"- <|> String <$ reserved "string"+ <|> Bytes' <$ reserved "bytes"+ <|> String (Just UUID) <$ reserved "uuid"+ <|> String' <$ reserved "string" <|> Array <$ reserved "array" <*> diamonds parseSchema <|> Map <$ reserved "map" <*> diamonds parseSchema <|> Union <$ reserved "union" <*> parseVector parseSchema@@ -200,6 +216,7 @@ <$> option [] parseAliases <* reserved "fixed" <*> parseTypeName <*> parens number+ <*> pure Nothing ) <|> try ( flip Enum@@ -221,17 +238,38 @@ parseFile :: Parsec e T.Text a -> String -> IO (Either (ParseErrorBundle T.Text e) a) parseFile p file = runParser p file <$> T.readFile file +(>>>=) :: Applicative m => Either a b -> (b -> m (Either a c)) -> m (Either a c)+Left x >>>= _ = pure (Left x)+Right y >>>= f = f y+ -- | Reads and parses a whole file and its imports, recursively.-readWithImports :: FilePath -- ^ base directory- -> FilePath -- ^ initial file- -> IO (Either (ParseErrorBundle T.Text Char) Protocol)+readWithImports ::+ -- | base directory+ FilePath ->+ -- | initial file+ FilePath ->+ IO (Either (ParseErrorBundle T.Text Char) Protocol) readWithImports baseDir initialFile = do initial <- parseFile parseProtocol (baseDir </> initialFile) case initial of Left e -> pure $ Left e Right p -> do- let imps = [i | IdlImport i <- imports p]- (lefts, rights) <- partitionEithers <$> traverse (readWithImports baseDir . T.unpack) imps+ possibleImps <- traverse (oneOfTwo . T.unpack) [i | IdlImport i <- S.toList $ imports p]+ (lefts, rights) <- partitionEithers <$> traverse (>>>= readWithImports baseDir) possibleImps pure $ case lefts of- e:_ -> Left e- _ -> Right $ foldl' (<>) p rights+ e : _ -> Left e+ _ -> Right $ S.foldl' (<>) p (S.fromList rights)+ where+ oneOfTwo :: FilePath -> IO (Either (ParseErrorBundle T.Text Char) FilePath)+ oneOfTwo p = do+ let dir = takeDirectory initialFile+ path1 = baseDir </> p+ path2 = baseDir </> dir </> p+ options <- (,) <$> doesFileExist path1 <*> doesFileExist path2+ pure $ case options of+ (True, False) -> Right p+ (False, True) -> Right $ dir </> p+ (False, False) -> runParser (fail $ "Import not found: " ++ p) initialFile ""+ (True, True)+ | normalise dir == "." -> Right p+ | otherwise -> runParser (fail $ "Duplicate files found: " ++ p) initialFile ""
src/Language/Avro/Types.hs view
@@ -1,10 +1,14 @@ -- | Language definition for AVRO (@.avdl@) files, -- as defined in <http://avro.apache.org/docs/1.8.2/spec.html>.-module Language.Avro.Types (- module Language.Avro.Types, Schema (..)-) where+module Language.Avro.Types+ ( module Language.Avro.Types,+ Schema (..),+ )+where import Data.Avro.Schema+import Data.Avro.Types.Value+import Data.Set import qualified Data.Text as T -- | Whole definition of protocol.@@ -12,11 +16,11 @@ = Protocol { ns :: Maybe Namespace, pname :: T.Text,- imports :: [ImportType],- types :: [Schema],- messages :: [Method]+ imports :: Set ImportType,+ types :: Set Schema,+ messages :: Set Method }- deriving (Eq, Show)+ deriving (Eq, Show, Ord) instance Semigroup Protocol where p1 <> p2 =@@ -30,7 +34,7 @@ -- | Newtype for the namespace of methods and protocols. newtype Namespace = Namespace [T.Text]- deriving (Eq, Show)+ deriving (Eq, Show, Ord) type Aliases = [TypeName] @@ -47,7 +51,7 @@ = IdlImport T.Text | ProtocolImport T.Text | SchemaImport T.Text- deriving (Eq, Show)+ deriving (Eq, Show, Ord) -- | Helper type for the arguments of 'Method'. data Argument@@ -55,7 +59,7 @@ { atype :: Schema, aname :: T.Text }- deriving (Eq, Show)+ deriving (Eq, Show, Ord) -- | Type for methods/messages that belong to a protocol. data Method@@ -66,4 +70,4 @@ throws :: Schema, oneway :: Bool }- deriving (Eq, Show)+ deriving (Eq, Show, Ord)
test/Spec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} module Main@@ -6,13 +7,18 @@ where import Data.Avro.Schema-import Data.Text as T hiding (tail)+import qualified Data.Text as T import Data.Vector (fromList) import Language.Avro.Parser import Language.Avro.Types import Test.Hspec+import Test.Hspec.Megaparsec import Text.Megaparsec (parse)+import Text.Megaparsec.Error +instance ShowErrorComponent Char where+ showErrorComponent = show+ enumTest :: T.Text enumTest = T.unlines@@ -62,105 +68,107 @@ describe "Parse annotations" $ do it "should parse namespaces" $ do parse parseNamespace "" "@namespace(\"mynamespace\")"- `shouldBe` (Right $ Namespace ["mynamespace"])+ `shouldParse` Namespace ["mynamespace"] parse parseNamespace "" "@namespace(\"org.apache.avro.test\")"- `shouldBe` (Right $ Namespace ["org", "apache", "avro", "test"])+ `shouldParse` Namespace ["org", "apache", "avro", "test"] it "should parse ordering" $ do- parse parseOrder "" "@order(\"ascending\")" `shouldBe` Right Ascending- parse parseOrder "" "@order(\"descending\")" `shouldBe` Right Descending- parse parseOrder "" "@order(\"ignore\")" `shouldBe` Right Ignore+ parse parseOrder "" "@order(\"ascending\")" `shouldParse` Ascending+ parse parseOrder "" "@order(\"descending\")" `shouldParse` Descending+ parse parseOrder "" "@order(\"ignore\")" `shouldParse` Ignore it "should parse aliases" $ do parse parseAliases "" "@aliases([\"org.foo.KindOf\"])"- `shouldBe` Right [TN "KindOf" ["org", "foo"]]+ `shouldParse` [TN "KindOf" ["org", "foo"]] parse parseAliases "" "@aliases([\"org.old.OldRecord\", \"org.ancient.AncientRecord\"])"- `shouldBe` Right [TN "OldRecord" ["org", "old"], TN "AncientRecord" ["org", "ancient"]]+ `shouldParse` [TN "OldRecord" ["org", "old"], TN "AncientRecord" ["org", "ancient"]] it "should parse other annotations" $ do parse parseAnnotation "" "@java-class(\"java.util.ArrayList\")"- `shouldBe` (Right $ Annotation "java-class" "java.util.ArrayList")+ `shouldParse` Annotation "java-class" "java.util.ArrayList" parse parseAnnotation "" "@java-key-class(\"java.io.File\")"- `shouldBe` (Right $ Annotation "java-key-class" "java.io.File")+ `shouldParse` Annotation "java-key-class" "java.io.File" describe "Parse imports" $ do it "should parse idl" $ parse parseImport "" "import idl \"foo.avdl\";"- `shouldBe` (Right $ IdlImport "foo.avdl")+ `shouldParse` IdlImport "foo.avdl" it "should parse protocol" $ parse parseImport "" "import protocol \"foo.avpr\";"- `shouldBe` (Right $ ProtocolImport "foo.avpr")+ `shouldParse` ProtocolImport "foo.avpr" it "should parse schema" $ parse parseImport "" "import schema \"foo.avsc\";"- `shouldBe` (Right $ SchemaImport "foo.avsc")+ `shouldParse` SchemaImport "foo.avsc" describe "Parse Data.Avro.Schema" $ do it "should parse null" $- parse parseSchema "" "null" `shouldBe` Right Null+ parse parseSchema "" "null" `shouldParse` Null it "should parse boolean" $- parse parseSchema "" "boolean" `shouldBe` Right Boolean+ parse parseSchema "" "boolean" `shouldParse` Boolean it "should parse int" $- parse parseSchema "" "int" `shouldBe` Right Int+ parse parseSchema "" "int" `shouldParse` Int' it "should parse long" $- parse parseSchema "" "long" `shouldBe` Right Long+ parse parseSchema "" "long" `shouldParse` Long' it "should parse float" $- parse parseSchema "" "float" `shouldBe` Right Float+ parse parseSchema "" "float" `shouldParse` Float it "should parse double" $- parse parseSchema "" "double" `shouldBe` Right Double+ parse parseSchema "" "double" `shouldParse` Double+ it "should parse decimal" $ do+ parse parseDecimal "" "decimal(4)" `shouldParse` Decimal 4 0+ parse parseDecimal "" "decimal(15,2)" `shouldParse` Decimal 15 2 it "should parse bytes" $- parse parseSchema "" "bytes" `shouldBe` Right Bytes+ parse parseSchema "" "bytes" `shouldParse` Bytes' it "should parse string" $- parse parseSchema "" "string" `shouldBe` Right String+ parse parseSchema "" "string" `shouldParse` String'+ it "should parse uuid" $+ parse parseSchema "" "uuid" `shouldParse` String (Just UUID) it "should parse array" $ do- parse parseSchema "" "array<int>" `shouldBe` (Right $ Array Int)+ parse parseSchema "" "array<int>" `shouldParse` Array Int' parse parseSchema "" "array<array<string>>"- `shouldBe` (Right $ Array $ Array String)+ `shouldParse` Array (Array String') it "should parse map" $ do- parse parseSchema "" "map<int>" `shouldBe` (Right $ Map Int)+ parse parseSchema "" "map<int>" `shouldParse` Map Int' parse parseSchema "" "map<map<string>>"- `shouldBe` (Right $ Map $ Map String)+ `shouldParse` Map (Map String') it "should parse unions" $ parse parseSchema "" "union { string, int, null }"- `shouldBe` (Right $ Union $ fromList [String, Int, Null])+ `shouldParse` Union (fromList [String', Int', Null]) it "should parse fixeds" $ do parse parseSchema "" "fixed MD5(16)"- `shouldBe` (Right $ Fixed (TN "MD5" []) [] 16)+ `shouldParse` Fixed (TN "MD5" []) [] 16 Nothing parse parseSchema "" "@aliases([\"org.foo.MD5\"])\nfixed MD5(16)"- `shouldBe` (Right $ Fixed (TN "MD5" []) ["org.foo.MD5"] 16)+ `shouldParse` Fixed (TN "MD5" []) ["org.foo.MD5"] 16 Nothing it "should parse enums" $ do parse parseSchema "" enumTest- `shouldBe` (Right $ Enum (TN "Kind" []) [TN "KindOf" ["org", "foo"]] Nothing (fromList ["FOO", "BAR", "BAZ"]))+ `shouldParse` Enum (TN "Kind" []) [TN "KindOf" ["org", "foo"]] Nothing (fromList ["FOO", "BAR", "BAZ"]) parse parseSchema "" "enum Suit { SPADES, DIAMONDS, CLUBS, HEARTS }"- `shouldBe` (Right $ Enum (TN "Suit" []) [] Nothing (fromList ["SPADES", "DIAMONDS", "CLUBS", "HEARTS"]))+ `shouldParse` Enum (TN "Suit" []) [] Nothing (fromList ["SPADES", "DIAMONDS", "CLUBS", "HEARTS"]) it "should parse named types" $ parse parseSchema "" "example.seed.server.protocol.avro.PeopleResponse"- `shouldBe` ( Right $ NamedType $ TN- { baseName = "PeopleResponse",- namespace = ["example", "seed", "server", "protocol", "avro"]- }- )+ `shouldParse` NamedType+ ( TN+ { baseName = "PeopleResponse",+ namespace = ["example", "seed", "server", "protocol", "avro"]+ }+ ) it "should parse simple records" $ parse parseSchema "" simpleRecord- `shouldBe` ( Right $- Record- (TN "Person" [])- [TN "Person" ["org", "foo"]]- Nothing -- docs are ignored for now...- Nothing -- order is ignored for now...- [ Field "name" [] Nothing Nothing String Nothing,- Field "age" [] Nothing Nothing Int Nothing- ]- )+ `shouldParse` Record+ (TN "Person" [])+ [TN "Person" ["org", "foo"]]+ Nothing -- docs are ignored for now...+ Nothing -- order is ignored for now...+ [ Field "name" [] Nothing Nothing String' Nothing,+ Field "age" [] Nothing Nothing Int' Nothing+ ] it "should parse complex records" $ parse parseSchema "" complexRecord- `shouldBe` ( Right $- Record- (TN "TestRecord" [])- []- Nothing -- docs are ignored for now...- Nothing -- order is ignored for now...- [ Field "name" [] Nothing (Just Ignore) String Nothing,- Field "kind" [] Nothing (Just Descending) (NamedType "Kind") Nothing,- Field "hash" [] Nothing Nothing (NamedType "MD5") Nothing,- Field "nullableHash" ["hash"] Nothing Nothing (Union $ fromList [NamedType "MD5", Null]) Nothing,- Field "arrayOfLongs" [] Nothing Nothing (Array Long) Nothing- ]- )+ `shouldParse` Record+ (TN "TestRecord" [])+ []+ Nothing -- docs are ignored for now...+ Nothing -- order is ignored for now...+ [ Field "name" [] Nothing (Just Ignore) String' Nothing,+ Field "kind" [] Nothing (Just Descending) (NamedType "Kind") Nothing,+ Field "hash" [] Nothing Nothing (NamedType "MD5") Nothing,+ Field "nullableHash" ["hash"] Nothing Nothing (Union $ fromList [NamedType "MD5", Null]) Nothing,+ Field "arrayOfLongs" [] Nothing Nothing (Array Long') Nothing+ ] describe "Parse protocols" $ do let getPerson = Method@@ -171,41 +179,32 @@ False it "should parse with imports" $ parse parseProtocol "" (T.unlines . tail $ simpleProtocol)- `shouldBe` ( Right $- Protocol- Nothing- "PeopleService"- [IdlImport "People.avdl"]- []- [getPerson]- )+ `shouldParse` Protocol Nothing "PeopleService" [IdlImport "People.avdl"] [] [getPerson] it "should parse with namespace" $ parse parseProtocol "" (T.unlines simpleProtocol)- `shouldBe` ( Right $- Protocol- (Just (Namespace ["example", "seed", "server", "protocol", "avro"]))- "PeopleService"- [IdlImport "People.avdl"]- []- [getPerson]- )+ `shouldParse` Protocol+ (Just (Namespace ["example", "seed", "server", "protocol", "avro"]))+ "PeopleService"+ [IdlImport "People.avdl"]+ []+ [getPerson] describe "Parse services" $ do it "should parse simple messages" $ parse parseMethod "" "string hello(string greeting);"- `shouldBe` (Right $ Method "hello" [Argument String "greeting"] String Null False)+ `shouldParse` Method "hello" [Argument String' "greeting"] String' Null False it "should parse more simple messages" $ parse parseMethod "" "bytes echoBytes(bytes data);"- `shouldBe` (Right $ Method "echoBytes" [Argument Bytes "data"] Bytes Null False)+ `shouldParse` Method "echoBytes" [Argument Bytes' "data"] Bytes' Null False it "should parse custom type messages" $- let custom = NamedType "TestRecord" in- parse parseMethod "" "TestRecord echo(TestRecord `record`);"- `shouldBe` (Right $ Method "echo" [Argument custom "record"] custom Null False)+ let custom = NamedType "TestRecord"+ in parse parseMethod "" "TestRecord echo(TestRecord `record`);"+ `shouldParse` Method "echo" [Argument custom "record"] custom Null False it "should parse multiple argument messages" $ parse parseMethod "" "int add(int arg1, int arg2);"- `shouldBe` (Right $ Method "add" [Argument Int "arg1", Argument Int "arg2"] Int Null False)+ `shouldParse` Method "add" [Argument Int' "arg1", Argument Int' "arg2"] Int' Null False it "should parse escaped and throwing messages" $ parse parseMethod "" "void `error`() throws TestError;"- `shouldBe` (Right $ Method "error" [] Null (NamedType $ TN "TestError" []) False)+ `shouldParse` Method "error" [] Null (NamedType $ TN "TestError" []) False it "should parse oneway messages" $ parse parseMethod "" "void ping() oneway;"- `shouldBe` (Right $ Method "ping" [] Null Null True)+ `shouldParse` Method "ping" [] Null Null True