{-# LANGUAGE OverloadedStrings #-}
module Backend.Python (generatePythonCode) where
import qualified Convex.Action.Parser as Action
import qualified Convex.Parser as P
import qualified Convex.Schema.Parser as Schema
import Data.Char (isUpper, toLower, toUpper)
import Data.List (intercalate, isPrefixOf, nub, partition)
import qualified Data.Map.Strict as Map
import Data.Maybe (catMaybes)
import qualified Data.Set as Set
import PathTree
data Definition
= Definition
{ Definition -> String
defName :: String,
Definition -> Set String
defDeps :: Set.Set String,
Definition -> String
defCode :: String
}
deriving (Int -> Definition -> ShowS
[Definition] -> ShowS
Definition -> String
(Int -> Definition -> ShowS)
-> (Definition -> String)
-> ([Definition] -> ShowS)
-> Show Definition
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Definition -> ShowS
showsPrec :: Int -> Definition -> ShowS
$cshow :: Definition -> String
show :: Definition -> String
$cshowList :: [Definition] -> ShowS
showList :: [Definition] -> ShowS
Show, Definition -> Definition -> Bool
(Definition -> Definition -> Bool)
-> (Definition -> Definition -> Bool) -> Eq Definition
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Definition -> Definition -> Bool
== :: Definition -> Definition -> Bool
$c/= :: Definition -> Definition -> Bool
/= :: Definition -> Definition -> Bool
Eq, Eq Definition
Eq Definition =>
(Definition -> Definition -> Ordering)
-> (Definition -> Definition -> Bool)
-> (Definition -> Definition -> Bool)
-> (Definition -> Definition -> Bool)
-> (Definition -> Definition -> Bool)
-> (Definition -> Definition -> Definition)
-> (Definition -> Definition -> Definition)
-> Ord Definition
Definition -> Definition -> Bool
Definition -> Definition -> Ordering
Definition -> Definition -> Definition
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Definition -> Definition -> Ordering
compare :: Definition -> Definition -> Ordering
$c< :: Definition -> Definition -> Bool
< :: Definition -> Definition -> Bool
$c<= :: Definition -> Definition -> Bool
<= :: Definition -> Definition -> Bool
$c> :: Definition -> Definition -> Bool
> :: Definition -> Definition -> Bool
$c>= :: Definition -> Definition -> Bool
>= :: Definition -> Definition -> Bool
$cmax :: Definition -> Definition -> Definition
max :: Definition -> Definition -> Definition
$cmin :: Definition -> Definition -> Definition
min :: Definition -> Definition -> Definition
Ord)
topologicalSort :: [Definition] -> [Definition]
topologicalSort :: [Definition] -> [Definition]
topologicalSort [Definition]
defs = [Definition] -> [Definition] -> Set String -> [Definition]
go [Definition]
defs [] ([String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList ([String] -> Set String) -> [String] -> Set String
forall a b. (a -> b) -> a -> b
$ (Definition -> String) -> [Definition] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Definition -> String
defName [Definition]
defs)
where
go :: [Definition] -> [Definition] -> Set String -> [Definition]
go [] [Definition]
sorted Set String
_ = [Definition]
sorted
go [Definition]
remaining [Definition]
sorted Set String
definedNames =
let ([Definition]
ready, [Definition]
pending) = (Definition -> Bool)
-> [Definition] -> ([Definition], [Definition])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (\Definition
d -> Set String -> Bool
forall a. Set a -> Bool
Set.null (Definition -> Set String
defDeps Definition
d Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.intersection` Set String
definedNames)) [Definition]
remaining
in if [Definition] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Definition]
ready Bool -> Bool -> Bool
&& Bool -> Bool
not ([Definition] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Definition]
pending)
then String -> [Definition]
forall a. HasCallStack => String -> a
error (String
"Circular dependency detected in definitions: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
forall a. Show a => a -> String
show ((Definition -> String) -> [Definition] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Definition -> String
defName [Definition]
pending))
else
let newSorted :: [Definition]
newSorted = [Definition]
sorted [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
ready
newDefinedNames :: Set String
newDefinedNames = Set String
definedNames Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` ([String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList ([String] -> Set String) -> [String] -> Set String
forall a b. (a -> b) -> a -> b
$ (Definition -> String) -> [Definition] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Definition -> String
defName [Definition]
ready)
in [Definition] -> [Definition] -> Set String -> [Definition]
go [Definition]
pending [Definition]
newSorted Set String
newDefinedNames
indent :: Int -> String -> String
indent :: Int -> ShowS
indent Int
n String
s = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
4) Char
' ' String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s
generatePythonCode :: P.ParsedProject -> String
generatePythonCode :: ParsedProject -> String
generatePythonCode ParsedProject
project =
let constantDefs :: [Definition]
constantDefs = Map String ConvexType -> [Definition]
generateAllConstants (ParsedProject -> Map String ConvexType
P.ppConstants ParsedProject
project)
tableDefs :: [Definition]
tableDefs = Schema -> [Definition]
generateAllTables (ParsedProject -> Schema
P.ppSchema ParsedProject
project)
(Definition
apiDef, [Definition]
apiNestedDefs) = [ConvexFunction] -> (Definition, [Definition])
generateApiClass (ParsedProject -> [ConvexFunction]
P.ppFunctions ParsedProject
project)
allDefs :: [Definition]
allDefs = [Definition] -> [Definition]
forall a. Eq a => [a] -> [a]
nub ([Definition] -> [Definition]) -> [Definition] -> [Definition]
forall a b. (a -> b) -> a -> b
$ [Definition]
constantDefs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
tableDefs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
apiNestedDefs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition
apiDef]
sortedDefs :: [Definition]
sortedDefs = [Definition] -> [Definition]
topologicalSort [Definition]
allDefs
definitionsCode :: String
definitionsCode = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (Definition -> String) -> [Definition] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Definition -> String
defCode [Definition]
sortedDefs
aliasesCode :: String
aliasesCode = Schema -> String
generateAliases (ParsedProject -> Schema
P.ppSchema ParsedProject
project)
in [String] -> String
unlines
[ String
generateHeader,
String
definitionsCode,
String
aliasesCode
]
generateHeader :: String
=
[String] -> String
unlines
[ String
"from typing import Any, Generic, Iterator, Literal, TypeVar",
String
"",
String
"from convex import ConvexClient, ConvexInt64",
String
"from pydantic import BaseModel, Field, TypeAdapter, ValidationError",
String
"from pydantic_core import core_schema",
String
"",
String
"",
String
"class PydanticConvexInt64(ConvexInt64):",
String
" @classmethod",
String
" def __get_pydantic_core_schema__(cls, s, h) -> core_schema.CoreSchema:",
String
" from_int_schema = core_schema.no_info_after_validator_function(cls, core_schema.int_schema())",
String
"",
String
" def validate_from_instance(v):",
String
" return PydanticConvexInt64(v.value)",
String
"",
String
" from_instance_schema = core_schema.no_info_after_validator_function(",
String
" validate_from_instance, core_schema.is_instance_schema(ConvexInt64)",
String
" )",
String
"",
String
" return core_schema.union_schema([from_instance_schema, from_int_schema])",
String
"",
String
"T = TypeVar('T')",
String
"class Id(str, Generic[T]):",
String
" @classmethod",
String
" def __get_pydantic_core_schema__(cls, s, h) -> core_schema.CoreSchema:",
String
" return core_schema.no_info_after_validator_function(cls, core_schema.str_schema())",
String
""
]
generateAllConstants :: Map.Map String Schema.ConvexType -> [Definition]
generateAllConstants :: Map String ConvexType -> [Definition]
generateAllConstants Map String ConvexType
constants =
((String, ConvexType) -> [Definition])
-> [(String, ConvexType)] -> [Definition]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String -> [Definition]
generateConstant (String -> [Definition])
-> ((String, ConvexType) -> String)
-> (String, ConvexType)
-> [Definition]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, ConvexType) -> String
forall a b. (a, b) -> a
fst) (Map String ConvexType -> [(String, ConvexType)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String ConvexType
constants)
where
generateConstant :: String -> [Definition]
generateConstant :: String -> [Definition]
generateConstant String
name =
let constType :: ConvexType
constType = Map String ConvexType
constants Map String ConvexType -> String -> ConvexType
forall k a. Ord k => Map k a -> k -> a
Map.! String
name
(String
pyType, Bool
_, Bool
_, [Definition]
nestedDefs, Set String
deps) = String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts String
name ConvexType
constType
code :: String
code = String
name String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
pyType
definition :: Definition
definition = Definition {defName :: String
defName = String
name, defDeps :: Set String
defDeps = Set String
deps, defCode :: String
defCode = String
code}
in Definition
definition Definition -> [Definition] -> [Definition]
forall a. a -> [a] -> [a]
: [Definition]
nestedDefs
generateAllTables :: Schema.Schema -> [Definition]
generateAllTables :: Schema -> [Definition]
generateAllTables (Schema.Schema [Table]
tables) =
let ([Definition]
tableDefs, [[Definition]]
nestedDefs) = [(Definition, [Definition])] -> ([Definition], [[Definition]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(Definition, [Definition])] -> ([Definition], [[Definition]]))
-> [(Definition, [Definition])] -> ([Definition], [[Definition]])
forall a b. (a -> b) -> a -> b
$ (Table -> (Definition, [Definition]))
-> [Table] -> [(Definition, [Definition])]
forall a b. (a -> b) -> [a] -> [b]
map Table -> (Definition, [Definition])
generateTable [Table]
tables
in [Definition]
tableDefs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [[Definition]] -> [Definition]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Definition]]
nestedDefs
generateTable :: Schema.Table -> (Definition, [Definition])
generateTable :: Table -> (Definition, [Definition])
generateTable Table
table =
let className :: String
className = ShowS
toClassName (Table -> String
Schema.tableName Table
table)
idField :: Field
idField = String -> ConvexType -> Field
Schema.Field String
"_id" (String -> ConvexType
Schema.VId (Table -> String
Schema.tableName Table
table))
creationTimeField :: Field
creationTimeField = String -> ConvexType -> Field
Schema.Field String
"_creationTime" ConvexType
Schema.VNumber
allFields :: [Field]
allFields = [Field
idField, Field
creationTimeField] [Field] -> [Field] -> [Field]
forall a. [a] -> [a] -> [a]
++ Table -> [Field]
Schema.tableFields Table
table
([String]
fieldLines, [[Definition]]
nestedDefsFromFields, [Set String]
fieldDeps) = [(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String]))
-> [(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String])
forall a b. (a -> b) -> a -> b
$ (Field -> (String, [Definition], Set String))
-> [Field] -> [(String, [Definition], Set String)]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Field -> (String, [Definition], Set String)
generateField String
className) [Field]
allFields
tableCode :: String
tableCode =
[String] -> String
unlines
[ String
"class " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
className String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(BaseModel):",
[String] -> String
unlines [String]
fieldLines,
String
"",
Int -> ShowS
indent Int
1 String
"class Config:",
Int -> ShowS
indent Int
2 String
"populate_by_name: bool = True"
]
deps :: Set String
deps = String -> Set String -> Set String
forall a. Ord a => a -> Set a -> Set a
Set.delete String
className ([Set String] -> Set String
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions [Set String]
fieldDeps)
definition :: Definition
definition = Definition {defName :: String
defName = String
className, defDeps :: Set String
defDeps = Set String
deps, defCode :: String
defCode = String
tableCode}
in (Definition
definition, [[Definition]] -> [Definition]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Definition]]
nestedDefsFromFields)
generateAliases :: Schema.Schema -> String
generateAliases :: Schema -> String
generateAliases (Schema.Schema [Table]
tables) =
let header :: String
header = String
"\n# --- Singular Type Aliases for Ergonomics ---\n"
in String
header String -> ShowS
forall a. [a] -> [a] -> [a]
++ ([String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (Table -> String) -> [Table] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Table -> String
toAlias [Table]
tables)
where
toAlias :: Table -> String
toAlias Table
t = ShowS
toSingular (Table -> String
Schema.tableName Table
t) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
toClassName (Table -> String
Schema.tableName Table
t)
generateFunction :: Int -> Action.ConvexFunction -> (String, [Definition], Set.Set String)
generateFunction :: Int -> ConvexFunction -> (String, [Definition], Set String)
generateFunction Int
level ConvexFunction
func =
let funcName :: String
funcName = ConvexFunction -> String
Action.funcName ConvexFunction
func
(String
argSignature, String
payloadMapping, [Definition]
defsFromArgs, Set String
depsFromArgs) = String
-> [(String, ConvexType)]
-> (String, String, [Definition], Set String)
generateArgSignature String
funcName (ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func)
funcNameSnake :: String
funcNameSnake = ShowS
toSnakeCase String
funcName
(String
rawReturnHint, Bool
isModelReturn, [Definition]
defsFromReturn, Set String
depsFromReturn) = String -> ConvexType -> (String, Bool, [Definition], Set String)
getReturnType String
funcName (ConvexFunction -> ConvexType
Action.funcReturn ConvexFunction
func)
handlerCall :: String
handlerCall = case ConvexFunction -> FuncType
Action.funcType ConvexFunction
func of
FuncType
Action.Query -> String
"self._client.query"
FuncType
Action.Mutation -> String
"self._client.mutation"
FuncType
Action.Action -> String
"self._client.action"
fullFuncPath :: String
fullFuncPath = String
"\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ConvexFunction -> String
Action.funcPath ConvexFunction
func String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""
(String
finalReturnHint, String
tryBlock) = case ConvexFunction -> ConvexType
Action.funcReturn ConvexFunction
func of
ConvexType
Schema.VVoid ->
( String
"None",
[String] -> String
unlines
[ Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) (String
handlerCall String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
", payload)"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"return"
]
)
ConvexType
_ ->
let hint :: String
hint = String
rawReturnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" | None"
rawResultDeclaration :: String
rawResultDeclaration =
if Bool
isModelReturn
then String
"raw_result = "
else String
"raw_result: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
hint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = "
validationLogic :: String
validationLogic =
if Bool
isModelReturn
then
if String
"list[" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
rawReturnHint
then String
"TypeAdapter(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
rawReturnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
").validate_python(raw_result)"
else String
rawReturnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
".model_validate(raw_result)"
else String
"raw_result"
body :: String
body =
[String] -> String
unlines
[ Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) (String
rawResultDeclaration String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
handlerCall String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
", payload)"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"if raw_result is None:",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) String
"return None",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) (String
"return " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
validationLogic)
]
in (String
hint, String
body)
funcCode :: String
funcCode =
[String] -> String
unlines
[ Int -> ShowS
indent Int
level (String
"def " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(self, " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
argSignature String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") -> " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
finalReturnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"\"\"\"Wraps the " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++ FuncType -> String
forall a. Show a => a -> String
show (ConvexFunction -> FuncType
Action.funcType ConvexFunction
func) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
".\"\"\""),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"payload: dict[str, Any] = {" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
payloadMapping String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"}"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
"try:",
String
tryBlock,
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
"except ValidationError as e:",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) (String
"print(f\"Validation error in '" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"': {e}\")"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"raise",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
"except Exception as e:",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) (String
"print(f\"Error in '" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"': {e}\")"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"raise"
]
in (String
funcCode, [Definition]
defsFromArgs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
defsFromReturn, Set String
depsFromArgs Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set String
depsFromReturn)
generateSubscriptionFunction :: Int -> Action.ConvexFunction -> (String, [Definition], Set.Set String)
generateSubscriptionFunction :: Int -> ConvexFunction -> (String, [Definition], Set String)
generateSubscriptionFunction Int
level ConvexFunction
func =
let funcName :: String
funcName = ConvexFunction -> String
Action.funcName ConvexFunction
func
(String
argSignature, String
payloadMapping, [Definition]
defsFromArgs, Set String
depsFromArgs) = String
-> [(String, ConvexType)]
-> (String, String, [Definition], Set String)
generateArgSignature String
funcName (ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func)
funcNameSnake :: String
funcNameSnake = String
"subscribe_" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
toSnakeCase String
funcName
(String
returnHint, Bool
_, [Definition]
defsFromReturn, Set String
depsFromReturn) = String -> ConvexType -> (String, Bool, [Definition], Set String)
getReturnType String
funcName (ConvexFunction -> ConvexType
Action.funcReturn ConvexFunction
func)
finalReturnHint :: String
finalReturnHint = String
"Iterator[" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"]"
fullFuncPath :: String
fullFuncPath = String
"\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ConvexFunction -> String
Action.funcPath ConvexFunction
func String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""
adapterCreation :: String
adapterCreation = Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"adapter = TypeAdapter(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")")
validationLogic :: String
validationLogic = Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) String
"validated_result = adapter.validate_python(raw_result)"
funcCode :: String
funcCode =
[String] -> String
unlines
[ Int -> ShowS
indent Int
level (String
"def " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(self, " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
argSignature String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") -> " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
finalReturnHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"\"\"\"Subscribes to the " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" query.\"\"\""),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"payload: dict[str, Any] = {" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
payloadMapping String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"}"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (String
"raw_subscription = self._client.subscribe(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
", payload)"),
String
adapterCreation,
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
"for raw_result in raw_subscription:",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"try:",
String
validationLogic,
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) String
"yield validated_result",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"except ValidationError as e:",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) String
"print(f\"Validation error in subscription update: {e}\")",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) String
"continue"
]
in (String
funcCode, [Definition]
defsFromArgs [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
defsFromReturn, Set String
depsFromArgs Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set String
depsFromReturn)
generateApiStructure :: Int -> PathTree -> ([String], [String], [Definition], Set.Set String)
generateApiStructure :: Int -> PathTree -> ([String], [String], [Definition], Set String)
generateApiStructure Int
level (DirNode Map String PathTree
dir) =
let ([String]
inits, [String]
defs, [Definition]
nestedDefs, Set String
deps) = (([String], [String], [Definition], Set String)
-> (String, PathTree)
-> ([String], [String], [Definition], Set String))
-> ([String], [String], [Definition], Set String)
-> [(String, PathTree)]
-> ([String], [String], [Definition], Set String)
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl ([String], [String], [Definition], Set String)
-> (String, PathTree)
-> ([String], [String], [Definition], Set String)
processEntry ([], [], [], Set String
forall a. Set a
Set.empty) (Map String PathTree -> [(String, PathTree)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String PathTree
dir)
in ([String]
inits, [String]
defs, [Definition]
nestedDefs, Set String
deps)
where
processEntry :: ([String], [String], [Definition], Set String)
-> (String, PathTree)
-> ([String], [String], [Definition], Set String)
processEntry ([String]
is, [String]
ds, [Definition]
nds, Set String
ds_deps) (String
_name, FuncNode ConvexFunction
func) =
let (String
funcDef, [Definition]
defsFromFunc, Set String
depsFromFunc) = Int -> ConvexFunction -> (String, [Definition], Set String)
generateFunction Int
level ConvexFunction
func
(String
subDef, [Definition]
defsFromSub, Set String
depsFromSub) =
if ConvexFunction -> FuncType
Action.funcType ConvexFunction
func FuncType -> FuncType -> Bool
forall a. Eq a => a -> a -> Bool
== FuncType
Action.Query
then Int -> ConvexFunction -> (String, [Definition], Set String)
generateSubscriptionFunction Int
level ConvexFunction
func
else (String
"", [], Set String
forall a. Set a
Set.empty)
in ([String]
is, [String]
ds [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
funcDef, String
subDef], [Definition]
nds [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
defsFromFunc [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
defsFromSub, Set String
ds_deps Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set String
depsFromFunc Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set String
depsFromSub)
processEntry ([String]
is, [String]
ds, [Definition]
nds, Set String
ds_deps) (String
name, DirNode Map String PathTree
subDir) =
let className :: String
className = ShowS
capitalize String
name
attrName :: String
attrName = ShowS
toSnakeCase String
name
initLine :: String
initLine = String
"self." String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
attrName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = self." String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
className String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(self._client)"
([String]
subInits, [String]
subDefs, [Definition]
defsFromSub, Set String
depsFromSub) = Int -> PathTree -> ([String], [String], [Definition], Set String)
generateApiStructure (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Map String PathTree -> PathTree
DirNode Map String PathTree
subDir)
classDef :: String
classDef =
[String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
[ String
"",
Int -> ShowS
indent Int
level (String
"class " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
className String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":"),
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) String
"def __init__(self, client: ConvexClient):",
Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) String
"self._client = client"
]
[String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> ShowS
indent (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2)) [String]
subInits
[String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
subDefs
in ([String]
is [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
initLine], [String]
ds [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
classDef], [Definition]
nds [Definition] -> [Definition] -> [Definition]
forall a. [a] -> [a] -> [a]
++ [Definition]
defsFromSub, Set String
ds_deps Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set String
depsFromSub)
generateApiStructure Int
_ (FuncNode ConvexFunction
_) = ([], [], [], Set String
forall a. Set a
Set.empty)
generateApiClass :: [Action.ConvexFunction] -> (Definition, [Definition])
generateApiClass :: [ConvexFunction] -> (Definition, [Definition])
generateApiClass [ConvexFunction]
funcs =
let tree :: PathTree
tree = [ConvexFunction] -> PathTree
buildPathTree [ConvexFunction]
funcs
([String]
initLines, [String]
definitionLines, [Definition]
nestedDefs, Set String
deps) = Int -> PathTree -> ([String], [String], [Definition], Set String)
generateApiStructure Int
1 PathTree
tree
header :: [String]
header =
[ String
"\n# --- API Client Class ---\n",
String
"class API:",
Int -> ShowS
indent Int
1 String
"\"\"\"A type-safe client for your Convex API.\"\"\"",
Int -> ShowS
indent Int
1 String
"def __init__(self, client: ConvexClient):",
Int -> ShowS
indent Int
2 String
"self._client = client"
]
body :: [String]
body = ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> ShowS
indent Int
2) [String]
initLines [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
definitionLines
apiCode :: String
apiCode = [String] -> String
unlines ([String]
header [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
body)
apiDef :: Definition
apiDef = Definition {defName :: String
defName = String
"API", defDeps :: Set String
defDeps = Set String
deps, defCode :: String
defCode = String
apiCode}
in (Definition
apiDef, [Definition]
nestedDefs)
generateArgSignature :: String -> [(String, Schema.ConvexType)] -> (String, String, [Definition], Set.Set String)
generateArgSignature :: String
-> [(String, ConvexType)]
-> (String, String, [Definition], Set String)
generateArgSignature String
funcName [(String, ConvexType)]
args =
let results :: [(String, (String, Bool, Bool, [Definition], Set String))]
results = ((String, ConvexType)
-> (String, (String, Bool, Bool, [Definition], Set String)))
-> [(String, ConvexType)]
-> [(String, (String, Bool, Bool, [Definition], Set String))]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
n, ConvexType
t) -> (String
n, String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts (ShowS
capitalize String
funcName String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
capitalize String
n) ConvexType
t)) [(String, ConvexType)]
args
sigParts :: [String]
sigParts = ((String, (String, Bool, Bool, [Definition], Set String))
-> String)
-> [(String, (String, Bool, Bool, [Definition], Set String))]
-> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
n, (String
t, Bool
_, Bool
_, [Definition]
_, Set String
_)) -> ShowS
toSnakeCase String
n String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
t) [(String, (String, Bool, Bool, [Definition], Set String))]
results
payloadParts :: [String]
payloadParts = ((String, (String, Bool, Bool, [Definition], Set String))
-> String)
-> [(String, (String, Bool, Bool, [Definition], Set String))]
-> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
n, (String, Bool, Bool, [Definition], Set String)
_) -> String
"\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
n String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
toSnakeCase String
n) [(String, (String, Bool, Bool, [Definition], Set String))]
results
nestedDefs :: [Definition]
nestedDefs = ((String, (String, Bool, Bool, [Definition], Set String))
-> [Definition])
-> [(String, (String, Bool, Bool, [Definition], Set String))]
-> [Definition]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(String
_, (String
_, Bool
_, Bool
_, [Definition]
defs, Set String
_)) -> [Definition]
defs) [(String, (String, Bool, Bool, [Definition], Set String))]
results
deps :: Set String
deps = [Set String] -> Set String
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions ([Set String] -> Set String) -> [Set String] -> Set String
forall a b. (a -> b) -> a -> b
$ ((String, (String, Bool, Bool, [Definition], Set String))
-> Set String)
-> [(String, (String, Bool, Bool, [Definition], Set String))]
-> [Set String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
_, (String
_, Bool
_, Bool
_, [Definition]
_, Set String
d)) -> Set String
d) [(String, (String, Bool, Bool, [Definition], Set String))]
results
argSignature :: String
argSignature = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
sigParts
in (if [String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
sigParts Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then String
argSignature else String
"*, " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
argSignature, String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
payloadParts, [Definition]
nestedDefs, Set String
deps)
getReturnType :: String -> Schema.ConvexType -> (String, Bool, [Definition], Set.Set String)
getReturnType :: String -> ConvexType -> (String, Bool, [Definition], Set String)
getReturnType String
funcName ConvexType
rt =
let (String
pyType, Bool
_, Bool
_, [Definition]
nestedDefs, Set String
deps) = String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts (ShowS
capitalize String
funcName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"Return") ConvexType
rt
isModel :: Bool
isModel = case ConvexType
rt of
Schema.VObject [(String, ConvexType)]
_ -> Bool
True
Schema.VArray (Schema.VObject [(String, ConvexType)]
_) -> Bool
True
Schema.VReference String
_ -> Bool
True
Schema.VArray (Schema.VReference String
_) -> Bool
True
ConvexType
_ -> Bool
False
in (String
pyType, Bool
isModel, [Definition]
nestedDefs, Set String
deps)
generateField :: String -> Schema.Field -> (String, [Definition], Set.Set String)
generateField :: String -> Field -> (String, [Definition], Set String)
generateField String
parentClassName Field
field =
let originalFieldName :: String
originalFieldName = Field -> String
Schema.fieldName Field
field
isSystemField :: Bool
isSystemField = String
"_" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
originalFieldName
fieldNameSnake :: String
fieldNameSnake = if Bool
isSystemField then ShowS
toSnakeCase (ShowS
forall a. HasCallStack => [a] -> [a]
tail String
originalFieldName) else ShowS
toSnakeCase String
originalFieldName
(String
pyType, Bool
isOpt, Bool
isArr, [Definition]
nested, Set String
deps) = String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts (String
parentClassName String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
capitalize String
originalFieldName) (Field -> ConvexType
Schema.fieldType Field
field)
fieldArgs :: String
fieldArgs =
let defaultArg :: String
defaultArg =
if Bool
isOpt
then if Bool
isArr then String
"default_factory=list" else String
"default=None"
else String
"..."
aliasArg :: Maybe String
aliasArg = if Bool
isSystemField then String -> Maybe String
forall a. a -> Maybe a
Just (String
"alias=\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
originalFieldName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\"") else Maybe String
forall a. Maybe a
Nothing
in String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " ([Maybe String] -> [String]
forall a. [Maybe a] -> [a]
catMaybes [String -> Maybe String
forall a. a -> Maybe a
Just String
defaultArg, Maybe String
aliasArg])
fieldDef :: String
fieldDef = String
fieldNameSnake String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
pyType String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = Field(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
fieldArgs String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
in (Int -> ShowS
indent Int
1 String
fieldDef, [Definition]
nested, Set String
deps)
toPythonTypeParts :: String -> Schema.ConvexType -> (String, Bool, Bool, [Definition], Set.Set String)
toPythonTypeParts :: String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts String
nameHint ConvexType
typ = case ConvexType
typ of
ConvexType
Schema.VString -> (String
"str", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VNumber -> (String
"float", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VInt64 -> (String
"PydanticConvexInt64", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VFloat64 -> (String
"float", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VBoolean -> (String
"bool", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VBytes -> (String
"bytes", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VAny -> (String
"Any", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
ConvexType
Schema.VNull -> (String
"None", Bool
True, Bool
False, [], Set String
forall a. Set a
Set.empty)
Schema.VId String
t -> (String
"Id['" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
toClassName String
t String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"']", Bool
False, Bool
False, [], String -> Set String
forall a. a -> Set a
Set.singleton (ShowS
toClassName String
t))
Schema.VArray ConvexType
inner ->
let (String
innerType, Bool
isOpt, Bool
isArr, [Definition]
nested, Set String
deps) = String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts String
nameHint ConvexType
inner
in (String
"list[" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
innerType String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"]", Bool
isOpt, Bool
isArr, [Definition]
nested, Set String
deps)
Schema.VOptional ConvexType
inner ->
let (String
innerType, Bool
_, Bool
innerIsArray, [Definition]
nested, Set String
deps) = String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts String
nameHint ConvexType
inner
in (String
innerType String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" | None", Bool
True, Bool
innerIsArray, [Definition]
nested, Set String
deps)
Schema.VUnion [ConvexType]
types ->
let results :: [(String, Bool, Bool, [Definition], Set String)]
results = (ConvexType -> (String, Bool, Bool, [Definition], Set String))
-> [ConvexType] -> [(String, Bool, Bool, [Definition], Set String)]
forall a b. (a -> b) -> [a] -> [b]
map (String
-> ConvexType -> (String, Bool, Bool, [Definition], Set String)
toPythonTypeParts String
nameHint) [ConvexType]
types
pyTypes :: [String]
pyTypes = [String] -> [String]
forall a. Eq a => [a] -> [a]
nub ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ ((String, Bool, Bool, [Definition], Set String) -> String)
-> [(String, Bool, Bool, [Definition], Set String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
t, Bool
_, Bool
_, [Definition]
_, Set String
_) -> String
t) [(String, Bool, Bool, [Definition], Set String)]
results
nested :: [Definition]
nested = ((String, Bool, Bool, [Definition], Set String) -> [Definition])
-> [(String, Bool, Bool, [Definition], Set String)] -> [Definition]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(String
_, Bool
_, Bool
_, [Definition]
d, Set String
_) -> [Definition]
d) [(String, Bool, Bool, [Definition], Set String)]
results
deps :: Set String
deps = [Set String] -> Set String
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions ([Set String] -> Set String) -> [Set String] -> Set String
forall a b. (a -> b) -> a -> b
$ ((String, Bool, Bool, [Definition], Set String) -> Set String)
-> [(String, Bool, Bool, [Definition], Set String)] -> [Set String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
_, Bool
_, Bool
_, [Definition]
_, Set String
d) -> Set String
d) [(String, Bool, Bool, [Definition], Set String)]
results
in (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
" | " [String]
pyTypes, Bool
False, Bool
False, [Definition]
nested, Set String
deps)
Schema.VLiteral String
s -> (String
"Literal[\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\"]", Bool
False, Bool
False, [], Set String
forall a. Set a
Set.empty)
Schema.VReference String
n -> (String
n, Bool
False, Bool
False, [], String -> Set String
forall a. a -> Set a
Set.singleton String
n)
Schema.VObject [(String, ConvexType)]
fields ->
let className :: String
className = ShowS
capitalize String
nameHint String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"Object"
([String]
fieldLines, [[Definition]]
nestedDefs, [Set String]
fieldDeps) = [(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String]))
-> [(String, [Definition], Set String)]
-> ([String], [[Definition]], [Set String])
forall a b. (a -> b) -> a -> b
$ (Field -> (String, [Definition], Set String))
-> [Field] -> [(String, [Definition], Set String)]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Field -> (String, [Definition], Set String)
generateField String
className) (((String, ConvexType) -> Field)
-> [(String, ConvexType)] -> [Field]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
n, ConvexType
t) -> String -> ConvexType -> Field
Schema.Field String
n ConvexType
t) [(String, ConvexType)]
fields)
newModelCode :: String
newModelCode =
[String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
[ String
"class " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
className String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(BaseModel):",
[String] -> String
unlines [String]
fieldLines,
String
"",
Int -> ShowS
indent Int
1 String
"class Config:",
Int -> ShowS
indent Int
2 String
"populate_by_name: bool = True"
]
deps :: Set String
deps = [Set String] -> Set String
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions [Set String]
fieldDeps
newModelDef :: Definition
newModelDef = Definition {defName :: String
defName = String
className, defDeps :: Set String
defDeps = Set String
deps, defCode :: String
defCode = String
newModelCode}
in (String
className, Bool
False, Bool
False, Definition
newModelDef Definition -> [Definition] -> [Definition]
forall a. a -> [a] -> [a]
: [[Definition]] -> [Definition]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[Definition]]
nestedDefs, String -> Set String
forall a. a -> Set a
Set.singleton String
className)
ConvexType
Schema.VVoid -> (String
"None", Bool
True, Bool
False, [], Set String
forall a. Set a
Set.empty)
capitalize :: String -> String
capitalize :: ShowS
capitalize String
"" = String
""
capitalize (Char
c : String
cs) = Char -> Char
toUpper Char
c Char -> ShowS
forall a. a -> [a] -> [a]
: String
cs
toSingular :: String -> String
toSingular :: ShowS
toSingular String
s
| String -> Char
forall a. HasCallStack => [a] -> a
last String
s Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
's' = ShowS
capitalize (ShowS
forall a. HasCallStack => [a] -> [a]
init String
s)
| Bool
otherwise = ShowS
capitalize String
s
toClassName :: String -> String
toClassName :: ShowS
toClassName String
s = ShowS
capitalize String
s String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"Doc"
toSnakeCase :: String -> String
toSnakeCase :: ShowS
toSnakeCase String
"" = String
""
toSnakeCase (Char
c : String
cs) = Char -> Char
toLower Char
c Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
go String
cs
where
go :: ShowS
go (Char
c' : String
cs')
| Char -> Bool
isUpper Char
c' = Char
'_' Char -> ShowS
forall a. a -> [a] -> [a]
: Char -> Char
toLower Char
c' Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
go String
cs'
| Bool
otherwise = Char
c' Char -> ShowS
forall a. a -> [a] -> [a]
: ShowS
go String
cs'
go String
"" = String
""