convex-schema-parser 0.1.6.0 → 0.1.7.0
raw patch · 5 files changed
+78/−37 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−0
- README.md +13/−4
- convex-schema-parser.cabal +1/−1
- src/Backend/Python.hs +2/−1
- src/Backend/Rust.hs +56/−31
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for convex-schema-parser +## 0.1.7.0 -- 2025-07-21++* **Codegen Improvements Rust/Python:**+ * We now generated `<FunctionPrefix>ArgObject`s for convex functions which receive parameters.+ * We now enforce named parameters for the generated python functions.+ ## 0.1.6.0 -- 2025-07-21 * **Codegen Improvements Rust:**
README.md view
@@ -216,7 +216,10 @@ # Additionally, we use `pydantic` for type validation, so we raise these exceptions as well. try: project_id = convex_api.Id("prj_...")- project = api.functions.projects.get_project(project_id)+ # NOTE that we force named arguments so you do not accidentally refactor stuff in your convex db+ # and forget to adjust your other projects. See the annotation for the rust example below for+ # the reason.+ project = api.functions.projects.get_project(project_id=project_id) if project: print(f"Successfully fetched project: {project.project_name}") else:@@ -235,7 +238,7 @@ try: # 1. Call the generated `subscribe_*` method. This returns a generator instantly. tenant_id = convex_api.Id("tnt_...")- project_subscription = api.functions.queries.subscribe_fetch_projects(tenant_id)+ project_subscription = api.functions.queries.subscribe_fetch_projects(tenant_id=tenant_id) print("Subscribed to projects. Waiting for updates... (Press Ctrl+C to stop)") @@ -283,7 +286,13 @@ let project_id = Id::<ProjectsDoc>::new("prj_...".to_string()); // This corresponds to the function `getProject` in `convex/functions/projects.ts`.- match api.functions().projects().get_project(&project_id).await {+ // NOTE the use of an argument type:+ // We cannot rely on convex to output deterministic positional args for the type generated for convex+ // functions. And in case you reorder arguments of the same type (like having two or more strings with+ // semantic differences) this will simulate "named args".+ // Especially for generated code, this is safer and keeps the calling site consistent regardless+ // of refactors in the convex backend.+ match api.functions().projects().get_project(convex_api::types::FunctionsQueriesGetProjectArg { project_id }).await { Ok(Some(project)) => { println!("Successfully fetched project: {}", project.project_name.unwrap_or_default()); }@@ -309,7 +318,7 @@ async fn run_subscription() -> anyhow::Result<()> { // 1. Call the generated `subscribe_*` method. let tenant_id = convex_api::Id::<convex_api::types::TenantsDoc>::new("tnt_...".to_string());- let mut project_subscription = api.functions().queries().subscribe_fetch_projects(&tenant_id).await?;+ let mut project_subscription = api.functions().queries().subscribe_fetch_projects(convex_api::types::FunctionsQueriesFetchProjectsArgObject { tenant_id }).await?; println!("Subscribed to projects. Waiting for updates... (Press Ctrl+C to stop)");
convex-schema-parser.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: convex-schema-parser-version: 0.1.6.0+version: 0.1.7.0 license: MIT author: Norbert Dzikowski maintainer: lambdax.one@icloud.com
src/Backend/Python.hs view
@@ -278,7 +278,8 @@ payloadParts = map (\(n, _) -> "\"" ++ n ++ "\": " ++ toSnakeCase n) results nestedDefs = concatMap (\(_, (_, _, _, defs, _)) -> defs) results deps = Set.unions $ map (\(_, (_, _, _, _, d)) -> d) results- in (intercalate ", " sigParts, intercalate ", " payloadParts, nestedDefs, deps)+ argSignature = intercalate ", " sigParts+ in (if length sigParts == 0 then argSignature else "*, " ++ argSignature, intercalate ", " payloadParts, nestedDefs, deps) -- Helper to get the return type information. getReturnType :: String -> Schema.ConvexType -> (String, Bool, [Definition], Set.Set String)
src/Backend/Rust.hs view
@@ -385,47 +385,72 @@ generateFunction :: Action.ConvexFunction -> (String, [String]) generateFunction func = let funcName = Action.funcName func- (argSignature, nestedFromArgs) = generateArgSignature funcName (Action.funcArgs func)+ args = Action.funcArgs func+ fullFuncPath = Action.funcPath func ++ ":" ++ funcName+ (argSignature, nestedFromArgs) = generateArgSignatureStruct fullFuncPath args funcNameSnake = toSnakeCase funcName (returnHint, isNullable, nestedFromReturn) = getReturnType funcName (Action.funcReturn func) handlerCall = case Action.funcType func of Action.Query -> "query" Action.Mutation -> "mutation" Action.Action -> "action"- fullFuncPath = Action.funcPath func ++ ":" ++ funcName btreemapConstruction = generateBTreeMap (Action.funcArgs func) returnHandling = generateReturnHandling returnHint isNullable- funcCode =- unlines- [ indent 1 ("/// Wraps the `" ++ fullFuncPath ++ "` " ++ show (Action.funcType func) ++ "."),- indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self, " ++ argSignature ++ ") -> Result<" ++ returnHint ++ ", ApiError> {"),- btreemapConstruction,- indent 2 ("let result = self.client." ++ handlerCall ++ "(\"" ++ fullFuncPath ++ "\", btmap).await?;"),- returnHandling,- indent 1 "}"- ]+ funcCode = case args of+ [] ->+ unlines+ [ indent 1 ("/// Wraps the `" ++ fullFuncPath ++ "` " ++ show (Action.funcType func) ++ "."),+ indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self) -> Result<" ++ returnHint ++ ", ApiError> {"),+ btreemapConstruction,+ indent 2 ("let result = self.client." ++ handlerCall ++ "(\"" ++ fullFuncPath ++ "\", btmap).await?;"),+ returnHandling,+ indent 1 "}"+ ]+ _ ->+ unlines+ [ indent 1 ("/// Wraps the `" ++ fullFuncPath ++ "` " ++ show (Action.funcType func) ++ "."),+ indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self, arg: " ++ argSignature ++ ") -> Result<" ++ returnHint ++ ", ApiError> {"),+ btreemapConstruction,+ indent 2 ("let result = self.client." ++ handlerCall ++ "(\"" ++ fullFuncPath ++ "\", btmap).await?;"),+ returnHandling,+ indent 1 "}"+ ] in (funcCode, nestedFromArgs ++ nestedFromReturn) generateSubscriptionFunction :: Action.ConvexFunction -> (String, [String]) generateSubscriptionFunction func = let funcName = Action.funcName func- (argSignature, nestedFromArgs) = generateArgSignature funcName (Action.funcArgs func)+ args = Action.funcArgs func+ (argSignature, nestedFromArgs) = generateArgSignatureStruct fullFuncPath args funcNameSnake = "subscribe_" ++ toSnakeCase funcName (returnHint, _, nestedFromReturn) = getReturnType funcName (Action.funcReturn func) fullFuncPath = Action.funcPath func ++ ":" ++ funcName btreemapConstruction = generateBTreeMap (Action.funcArgs func)- funcCode =- unlines- [ indent 1 ("/// Subscribes to the `" ++ fullFuncPath ++ "` query."),- indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self, " ++ argSignature ++ ") -> Result<TypedSubscription<" ++ returnHint ++ ">, ApiError> {"),- btreemapConstruction,- indent 2 ("let raw_subscription = self.client.subscribe(\"" ++ fullFuncPath ++ "\", btmap).await?;"),- indent 2 "Ok(TypedSubscription {",- indent 3 "raw_subscription,",- indent 3 "_phantom: PhantomData,",- indent 2 "})",- indent 1 "}"- ]+ funcCode = case args of+ [] ->+ unlines+ [ indent 1 ("/// Subscribes to the `" ++ fullFuncPath ++ "` query."),+ indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self) -> Result<TypedSubscription<" ++ returnHint ++ ">, ApiError> {"),+ btreemapConstruction,+ indent 2 ("let raw_subscription = self.client.subscribe(\"" ++ fullFuncPath ++ "\", btmap).await?;"),+ indent 2 "Ok(TypedSubscription {",+ indent 3 "raw_subscription,",+ indent 3 "_phantom: PhantomData,",+ indent 2 "})",+ indent 1 "}"+ ]+ _ ->+ unlines+ [ indent 1 ("/// Subscribes to the `" ++ fullFuncPath ++ "` query."),+ indent 1 ("pub async fn " ++ funcNameSnake ++ "(&mut self, arg: " ++ argSignature ++ ") -> Result<TypedSubscription<" ++ returnHint ++ ">, ApiError> {"),+ btreemapConstruction,+ indent 2 ("let raw_subscription = self.client.subscribe(\"" ++ fullFuncPath ++ "\", btmap).await?;"),+ indent 2 "Ok(TypedSubscription {",+ indent 3 "raw_subscription,",+ indent 3 "_phantom: PhantomData,",+ indent 2 "})",+ indent 1 "}"+ ] in (funcCode, nestedFromArgs ++ nestedFromReturn) generateTypesModule :: P.ParsedProject -> [String] -> String@@ -528,18 +553,18 @@ fieldLine = serdeRename ++ serdeAttrs ++ "\n" ++ indent 2 ("pub " ++ fieldNameSnake ++ ": " ++ rustType ++ ",") in (fieldLine, nested) -generateArgSignature :: String -> [(String, Schema.ConvexType)] -> (String, [String])-generateArgSignature funcName args =- let results = map (\(n, t) -> toRustType (funcName ++ capitalize n) t) args- sigParts = zipWith (\(n, _) (ty, _) -> toSnakeCase n ++ ": " ++ toRustBorrowType ty) args results- nestedModels = concatMap snd results- in (intercalate ", " sigParts, nestedModels)+generateArgSignatureStruct :: String -> [(String, Schema.ConvexType)] -> (String, [String])+generateArgSignatureStruct _ [] = ("", [])+generateArgSignatureStruct fullFuncName args =+ let argStructName = toPascalCase $ map (\c -> if (c == '/' || c == ':') then '_' else c) $ fullFuncName ++ "Arg"+ (argCode, subArgs) = generateConstant argStructName $ Schema.VObject args+ in ("types::" ++ argStructName ++ "Object", argCode : subArgs) generateBTreeMap :: [(String, Schema.ConvexType)] -> String generateBTreeMap [] = indent 2 "let btmap = BTreeMap::new();" generateBTreeMap btmap = let buildStmts (name, convexType) =- let varName = toSnakeCase name+ let varName = "arg." ++ toSnakeCase name in case convexType of Schema.VObject _ -> indent 1 ("btmap.insert(\"" ++ name ++ "\".to_string(), " ++ varName ++ ".to_convex_value()?);") Schema.VOptional innerConvexType -> indent 1 ("if let Some(v) = " ++ varName ++ " { btmap.insert(\"" ++ name ++ "\".to_string(), " ++ fieldToConvexValue ("v", innerConvexType) ++ "); }")