( typeName: string, schema: JSONSchema7, ctx: RustCodegenCtx, description?: string, )
| 859 | // ── Struct emission ───────────────────────────────────────────────────────── |
| 860 | |
| 861 | function emitRustStruct( |
| 862 | typeName: string, |
| 863 | schema: JSONSchema7, |
| 864 | ctx: RustCodegenCtx, |
| 865 | description?: string, |
| 866 | ): void { |
| 867 | if (ctx.generatedNames.has(typeName)) return; |
| 868 | ctx.generatedNames.add(typeName); |
| 869 | |
| 870 | const required = new Set(schema.required || []); |
| 871 | const lines: string[] = []; |
| 872 | const desc = description || schema.description; |
| 873 | if (desc) { |
| 874 | for (const line of desc.split(/\r?\n/)) { |
| 875 | lines.push(`/// ${line}`); |
| 876 | } |
| 877 | } |
| 878 | pushRustExperimentalDocs(lines, isSchemaExperimental(schema) || ctx.experimentalTypeNames.has(typeName)); |
| 879 | if (isSchemaDeprecated(schema)) { |
| 880 | lines.push(...rustDeprecatedAttributes()); |
| 881 | } |
| 882 | const structVis = isSchemaInternal(schema) ? "pub(crate)" : "pub"; |
| 883 | |
| 884 | // Resolve field types up-front so we can decide whether `Default` can be |
| 885 | // derived. A required field whose bare type is non-default-able (e.g. an |
| 886 | // untagged enum, or another struct that already opted out) blocks the |
| 887 | // derive and propagates the opt-out to this struct. |
| 888 | interface FieldInfo { |
| 889 | propName: string; |
| 890 | prop: JSONSchema7; |
| 891 | isReq: boolean; |
| 892 | rustField: string; |
| 893 | rustType: string; |
| 894 | } |
| 895 | const fields: FieldInfo[] = []; |
| 896 | for (const [propName, propSchema] of Object.entries( |
| 897 | schema.properties || {}, |
| 898 | )) { |
| 899 | if (typeof propSchema !== "object") continue; |
| 900 | const prop = propSchema as JSONSchema7; |
| 901 | const isReq = required.has(propName); |
| 902 | const rustField = safeRustFieldName(propName); |
| 903 | const rustType = resolveRustType(prop, typeName, propName, isReq, ctx); |
| 904 | fields.push({ propName, prop, isReq, rustField, rustType }); |
| 905 | } |
| 906 | |
| 907 | const blocksDefault = fields.some( |
| 908 | (f) => f.isReq && ctx.nonDefaultableTypes.has(f.rustType), |
| 909 | ); |
| 910 | if (blocksDefault) { |
| 911 | ctx.nonDefaultableTypes.add(typeName); |
| 912 | lines.push("#[derive(Debug, Clone, Serialize, Deserialize)]"); |
| 913 | } else { |
| 914 | lines.push("#[derive(Debug, Clone, Default, Serialize, Deserialize)]"); |
| 915 | } |
| 916 | lines.push(`#[serde(rename_all = "camelCase")]`); |
| 917 | lines.push(`${structVis} struct ${typeName} {`); |
| 918 |
no test coverage detected
searching dependent graphs…