servant-csharp 0.0.4.0 → 0.0.5.0
raw patch · 3 files changed
+196/−40 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ CS: [classtemplate] :: GenerateCsConfig -> GenerateCsConfig -> IO String
+ CS: classCsForAPI :: IO String
+ CS: classCsForAPIWith :: GenerateCsConfig -> IO String
+ CS.JsonDotNet: [classtemplate] :: GenerateCsConfig -> GenerateCsConfig -> IO String
+ CS.JsonDotNet: classCsForAPI :: IO String
+ CS.JsonDotNet: classCsForAPIWith :: GenerateCsConfig -> IO String
+ CS.JsonDotNet: instance GHC.Show.Show CS.JsonDotNet.FieldType
- CS: GenerateCsConfig :: String -> (forall api. (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => GenerateCsConfig -> Proxy api -> IO String) -> (GenerateCsConfig -> IO String) -> (GenerateCsConfig -> IO String) -> [FilePath] -> GenerateCsConfig
+ CS: GenerateCsConfig :: String -> (GenerateCsConfig -> IO String) -> (forall api. (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => GenerateCsConfig -> Proxy api -> IO String) -> (GenerateCsConfig -> IO String) -> (GenerateCsConfig -> IO String) -> [FilePath] -> GenerateCsConfig
- CS.JsonDotNet: GenerateCsConfig :: String -> (forall api. (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => GenerateCsConfig -> Proxy api -> IO String) -> (GenerateCsConfig -> IO String) -> (GenerateCsConfig -> IO String) -> [FilePath] -> GenerateCsConfig
+ CS.JsonDotNet: GenerateCsConfig :: String -> (GenerateCsConfig -> IO String) -> (forall api. (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => GenerateCsConfig -> Proxy api -> IO String) -> (GenerateCsConfig -> IO String) -> (GenerateCsConfig -> IO String) -> [FilePath] -> GenerateCsConfig
Files
- servant-csharp.cabal +1/−1
- src/CS.hs +3/−1
- src/CS/JsonDotNet.hs +192/−38
servant-csharp.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: servant-csharp-version: 0.0.4.0+version: 0.0.5.0 synopsis: Generate servant client library for C# description: Generate servant client library for C# homepage: https://github.com/cutsea110/servant-csharp.git
src/CS.hs view
@@ -1,4 +1,6 @@-module CS ( apiCsForAPI+module CS ( classCsForAPI+ , classCsForAPIWith+ , apiCsForAPI , apiCsForAPIWith , enumCsForAPI , enumCsForAPIWith
src/CS/JsonDotNet.hs view
@@ -4,7 +4,9 @@ {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}-module CS.JsonDotNet ( apiCsForAPI+module CS.JsonDotNet ( classCsForAPI+ , classCsForAPIWith+ , apiCsForAPI , apiCsForAPIWith , enumCsForAPI , enumCsForAPIWith@@ -34,6 +36,7 @@ data GenerateCsConfig = GenerateCsConfig { namespace :: String+ , classtemplate :: GenerateCsConfig -> IO String , apitemplate :: forall api. (HasForeign CSharp Text api, GenerateList Text (Foreign Text api))@@ -47,6 +50,7 @@ def :: GenerateCsConfig def = GenerateCsConfig { namespace = "ServantClientAPI"+ , classtemplate = defClassTemplate , apitemplate = defAPITemplate , enumtemplate = defEnumTemplate , convtemplate = defConvTemplate@@ -54,6 +58,88 @@ } --------------------------------------------------------------------------+isDatatypeDecl :: Decl -> Bool+isDatatypeDecl (DataDecl _ DataType _ _ _ [qcon] _) = True+isDatatypeDecl _ = False++data FieldType = TInt+ | TString+ | TDay+ | TUTCTime+ | TEnum String+ | TGeneral String+ | TNewtype String FieldType+ | TList FieldType+ | TNullable FieldType++instance Show FieldType where+ show TInt = "int"+ show TString = "string"+ show TDay = "DateTime"+ show TUTCTime = "DateTime"+ show (TEnum s) = s+ show (TGeneral s) = s+ show (TNewtype s _) = s+ show (TList t) = "List<"<>show t<>">"+ show (TNullable TInt) = "int?"+ show (TNullable TString) = "string"+ show (TNullable TDay) = "DateTime?"+ show (TNullable TUTCTime) = "DateTime?"+ show (TNullable (TEnum t)) = show (TEnum t)<>"?"+ show (TNullable (TNewtype s TString)) = s+ show (TNullable (TNewtype s _)) = "Nullable<"<>s<>">"+ show (TNullable t) = "Nullable<"<>show t<>">"++showCSharpOriginalType :: FieldType -> String+showCSharpOriginalType TInt = "System.Int64"+showCSharpOriginalType TString = "System.String"+showCSharpOriginalType _ = error "don't support this type."++classTypes :: GenerateCsConfig -> IO [(String, [(String, FieldType)])]+classTypes conf = do+ enums <- fmap (map fst) $ enumTypes conf+ aliases <- usingAliases conf+ classTypesFromFiles enums aliases (sources conf)+ where+ classTypesFromFiles :: [String] -> [(String, FieldType)] -> [FilePath]+ -> IO [(String, [(String, FieldType)])]+ classTypesFromFiles enums aliases hss+ = return . concat =<< mapM (classTypesFromFile enums aliases) hss++ classTypesFromFile :: [String] -> [(String, FieldType)] -> FilePath+ -> IO [(String, [(String, FieldType)])]+ classTypesFromFile enums aliases hs = do+ ParseOk (Module _ _ _ _ _ _ decls) <- parseFile hs+ let xs = filter isDatatypeDecl decls+ return $ map toClass xs+ where+ toClass (DataDecl _ _ _ _ _ [qcon] _)+ = toClass' qcon+ toClass' (QualConDecl _ _ _ (RecDecl (Ident name) fs))+ = (name, map field fs)+ field ((Ident fname):[], ts)+ = (fname, toType ts)+ toType :: Type -> FieldType+ toType (TyCon (UnQual (Ident t)))+ = case t of+ "String" -> TString+ "Text" -> TString+ "Int" -> TInt+ "Integer" -> TInt+ "Day" -> TDay+ "UTCTime" -> TUTCTime+ _ -> if t `elem` enums+ then TEnum t+ else maybe (TGeneral t) (TNewtype t)+ $ lookup t aliases+ toType (TyApp (TyCon (UnQual (Ident "Maybe"))) t)+ = case toType t of+ TList t -> TList t+ t -> TNullable t+ toType (TyList t) = TList (toType t)+ toType _ = error "don't support this Type"++-------------------------------------------------------------------------- isEnumLikeDataDecl :: Decl -> Bool isEnumLikeDataDecl (DataDecl _ DataType _ _ _ xs _) = all isEnumLikeConDecl xs@@ -63,50 +149,66 @@ isEnumLikeConDecl (QualConDecl _ _ _ (ConDecl _ [])) = True isEnumLikeConDecl _ = False -enumTypesFromFiles :: [FilePath] -> IO [(String, [String])]-enumTypesFromFiles hss- = return . concat =<< mapM enumTypesFromFile hss--enumTypesFromFile :: FilePath -> IO [(String, [String])]-enumTypesFromFile hs = do- ParseOk (Module _ _ _ _ _ _ decls) <- parseFile hs- let xs = filter isEnumLikeDataDecl decls- return $ map toTuple xs+enumTypes :: GenerateCsConfig -> IO [(String, [String])]+enumTypes = enumTypesFromFiles . sources where- conName :: QualConDecl -> String- conName (QualConDecl _ _ _ (ConDecl (Ident name) [])) = name- conName _ = error "invalid enum type"- toTuple (DataDecl _ _ _ (Ident name) _ xs _)- = (name, map conName xs)+ enumTypesFromFiles :: [FilePath] -> IO [(String, [String])]+ enumTypesFromFiles hss+ = return . concat =<< mapM enumTypesFromFile hss + enumTypesFromFile :: FilePath -> IO [(String, [String])]+ enumTypesFromFile hs = do+ ParseOk (Module _ _ _ _ _ _ decls) <- parseFile hs+ let xs = filter isEnumLikeDataDecl decls+ return $ map toTuple xs+ where+ conName :: QualConDecl -> String+ conName (QualConDecl _ _ _ (ConDecl (Ident name) [])) = name+ conName _ = error "invalid enum type"+ toTuple (DataDecl _ _ _ (Ident name) _ xs _)+ = (name, map conName xs)+ -------------------------------------------------------------------------- -- | TODO : more typeable isNewtypeDecl :: Decl -> Bool isNewtypeDecl (DataDecl _ NewType _ _ _ _ _) = True isNewtypeDecl _ = False -origType :: QualConDecl -> String-origType (QualConDecl _ _ _ (RecDecl _ [(_, TyCon (UnQual (Ident t)))]))- = case t of- "String" -> "System.String"- "Text" -> "System.String"- "Int" -> "System.Int64"- "Integer" -> "System.Int64"- t -> error "don't supported type. "<>t+isTypeDecl :: Decl -> Bool+isTypeDecl (TypeDecl _ _ _ _) = True+isTypeDecl _ = False -usingAliasesFromFiles :: [FilePath] -> IO [(String, String)]-usingAliasesFromFiles hss- = return . concat =<< mapM usingAliasesFromFile hss+origType :: QualConDecl -> FieldType+origType (QualConDecl _ _ _ (RecDecl _ [(_, tycon)]))+ = origType' tycon -usingAliasesFromFile :: FilePath -> IO [(String, String)]-usingAliasesFromFile hs = do- ParseOk (Module _ _ _ _ _ _ decls) <- parseFile hs- let xs = filter isNewtypeDecl decls- return $ map toTuple xs+origType' :: Type -> FieldType+origType' (TyCon (UnQual (Ident t)))+ = case t of+ "String" -> TString+ "Text" -> TString+ "Int" -> TInt+ "Integer" -> TInt+ t -> error ("don't supported type. "<>t)++usingAliases :: GenerateCsConfig -> IO [(String, FieldType)]+usingAliases = usingAliasesFromFiles . sources where- toTuple (DataDecl _ NewType _ (Ident name) _ [qcon] _)- = (name, origType qcon)+ usingAliasesFromFiles :: [FilePath] -> IO [(String, FieldType)]+ usingAliasesFromFiles hss+ = return . concat =<< mapM usingAliasesFromFile hss + usingAliasesFromFile :: FilePath -> IO [(String, FieldType)]+ usingAliasesFromFile hs = do+ ParseOk (Module _ _ _ _ _ _ decls) <- parseFile hs+ let xs = filter (\d -> isNewtypeDecl d || isTypeDecl d) decls+ return $ map toTuple xs+ where+ toTuple (DataDecl _ NewType _ (Ident name) _ [qcon] _)+ = (name, origType qcon)+ toTuple (TypeDecl _ (Ident name) _ tycon)+ = (name, origType' tycon)+ -------------------------------------------------------------------------- retType :: Req Text -> String retType = T.unpack . fromJust . view reqReturnType@@ -185,6 +287,12 @@ requestBodyExists :: Req Text -> Bool requestBodyExists = not . null . rqBody +classCsForAPI :: IO String+classCsForAPI = classCsForAPIWith def++classCsForAPIWith :: GenerateCsConfig -> IO String+classCsForAPIWith conf = (classtemplate conf) conf+ apiCsForAPI :: (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => Proxy api -> IO String@@ -207,11 +315,57 @@ converterCsForAPIWith :: GenerateCsConfig -> IO String converterCsForAPIWith conf = (convtemplate conf) conf +defClassTemplate :: GenerateCsConfig -> IO String+defClassTemplate conf = do+ uas <- usingAliases conf+ classes <- classTypes conf+ return [heredoc|+using Newtonsoft.Json;+using Newtonsoft.Converters;+using System;+using System.Collections.Generic;++#region type alias+$forall (n, t) <- uas+ using ${n} = ${showCSharpOriginalType t};+#endregion++namespace ${namespace conf}+{+ $forall (name, fields) <- classes+ #region ${name}+ [JsonObject("${name}")]+ public class ${name}+ {+ $forall (fname, ftype) <- fields+ $case ftype+ $of TDay+ [JsonProperty(PropertyName = "${fname}")]+ [JsonConverter(typeof(DayConverter))]+ $of TNullable TDay+ [JsonProperty(PropertyName = "${fname}")]+ [JsonConverter(typeof(DayConverter))]+ $of TEnum _+ [JsonProperty(PropertyName = "${fname}")]+ [JsonConverter(typeof(StringEnumConverter))]+ $of TNullable (TEnum _)+ [JsonProperty(PropertyName = "${fname}")]+ [JsonConverter(typeof(StringEnumConverter))]+ $of TList (TEnum _)+ [JsonProperty(PropertyName = "${fname}", ItemConverterType = typeof(StringEnumConverter))]+ $of _+ [JsonProperty(PropertyName = "${fname}")]+ public ${show ftype} ${fname} { get; set; }+ }+ #endregion+}+|]+ defAPITemplate :: (HasForeign CSharp Text api, GenerateList Text (Foreign Text api)) => GenerateCsConfig -> Proxy api -> IO String defAPITemplate conf api = do- usingAliases <- usingAliasesFromFiles $ sources conf+ uas <- usingAliases conf return [heredoc|/* generated by servant-csharp */ using Newtonsoft.Json; using System.Collections.Generic;@@ -223,8 +377,8 @@ using System.Threading.Tasks; #region type alias-$forall (n, t) <- usingAliases- using ${n} = ${t};+$forall (n, t) <- uas+ using ${n} = ${showCSharpOriginalType t}; #endregion namespace ${namespace conf}@@ -304,7 +458,7 @@ |] defEnumTemplate conf = do- es <- enumTypesFromFiles $ sources conf+ es <- enumTypes conf return [heredoc|/* generated by servant-csharp */ namespace ${namespace conf} {@@ -320,7 +474,7 @@ |] defConvTemplate conf = do- return [heredoc|+ return [heredoc|/* generated by servant-csharp */ using Newtonsoft.Json; using System;