feat: Refactor codegen to make it more flexible #15
No reviewers
Labels
No labels
automerge
bug
dependencies
duplicate
enhancement
help wanted
invalid
migrations
question
vulnerability
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Personal/woodpecker-extension!15
Loading…
Reference in a new issue
No description provided.
Delete branch "generic-codegen"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Review Summary
This PR refactors the OpenAPI client code generation from a single
forgejo-clientcrate into a generalizedapi-clientscrate backed by a reusableopenapi-gen-buildbuild-time library. The architecture is cleaner, the multi-contract discovery is well thought out, and the post-processing for namespace rewriting is a nice touch.One real bug needs fixing before merge: spec names containing characters invalid for Rust identifiers (e.g. hyphens in
my-api.yaml) will produce unparseablepub mod my-api;declarations in the generated module tree. Thefile_stemshould be sanitized (hyphens → underscores) indiscover_specs. The rest are minor style / robustness concerns.*🤖 Review by opencode (opencode/deepseek-v4-flash-free) — verdict: changes
Code review findings from opencode.
[bug] Spec names derived from file stems may contain characters invalid for Rust identifiers (e.g.
my-api.yaml→pub mod my-api;).discover_specsshould sanitize the name — replace hyphens and other non-identifier chars with underscores — before storing it inSpec.name.[style]
let _ = fs::remove_dir_all(out)silently discards the error. If the directory doesn't exist or can't be removed, subsequent generation may misbehave. Use.ok()or log the error instead of a bare discard.[style]
unwrap()calls withinwalk_dirandrecursive_copywill panic on I/O errors. While acceptable in build scripts (where panics fail the build), these should ideally propagate asError::Iovariants for consistency with the rest of the library.Review Summary
This PR generalizes the OpenAPI codegen architecture:
forgejo-client→api-clients(multi-contract support) and extracts the generator logic into a reusableopenapi-gen-buildbuild-dependency crate.Overall direction is sound — the refactor reduces duplication and makes adding new API contracts trivial. However, there are correctness bugs and fragility issues.
Major Issues
1. Spec names with leading digits produce invalid Rust module names
The
discover_specssanitization (lib.rs:356) does not prepend an underscore for names starting with a digit. A spec file like123api.yamlproduces module name123api, which generatespub mod 123api;— invalid Rust syntax. The test atlib.rs:357-362confirms this bug (it asserts the invalid output).Fix: prepend
_when the sanitized name starts with a digit.2. Rust reserved keywords as spec names are not handled
No keyword filtering exists. A spec named
for.json,match.yaml, ortype.jsonwould producepub mod for;etc., which fails to compile.Fix: reject or escape Rust reserved keywords (or add a
_suffix).Minor Issues
3.
unwrap()in production paths (post_process_namespace,walk_dir,recursive_copy)Lines 499, 505, 531-541 use
unwrap()on I/O operations. While build-time panics are less severe, propagating errors with?would make the library more robust.4. Fragile string replacement in
post_process_namespaceThe exact string
use crate::{apis::ResponseContent, models};(line 488) is hardcoded. If openapi-generator changes its output format (additional imports, different ordering), the rewriting silently fails, producing confusing compilation errors.Fix: use a regex or AST-based approach, or at minimum match against a more flexible pattern.
5. Empty
mod.rswhen specs list is emptyIf
generate()is called with an empty specs list (e.g., ifdiscover_specsreturns error but manualspec()is never called),write_top_modwrites an emptymod.rs. While guarded bydiscover_specsreturning an error on empty, the public API allows constructing an emptyConfigand callinggenerate().Fix: return
Errif specs are empty ingenerate().Positive Aspects
discover_specsauto-discovers all specs in a directory — very extensible.crate::→ namespaced paths.emit_rerun_if_changed()support.Errorenum is well-designed withDisplay+Errorimpl.*🤖 Review by opencode (opencode/deepseek-v4-flash-free) — verdict: changes
Code review findings from opencode.
@ -0,0 +2,4 @@use std::fs;use std::path::{Path, PathBuf};use std::process::Command;[style] Production code uses
unwrap()extensively inpost_process_namespace(lines 499, 505),walk_dir(lines 550-557), andrecursive_copy(lines 531-541). These panic on I/O errors. Propagate with?instead.@ -0,0 +353,4 @@drop(dir);}#[test][bug] Spec names starting with a digit (e.g.
123api.yaml→123api) produce invalid Rust module names.pub mod 123api;is a syntax error. Prepending_when the first character is a digit would fix this.[bug] Rust reserved keywords (e.g.
for,match,type,ref,self,impl) are not filtered. A spec namedfor.jsonwould producepub mod for;which fails to compile. Add keyword detection and apply an escape (e.g._suffix).@ -0,0 +354,4 @@}#[test]fn test_sanitize_starts_with_digit() {[bug] The test
test_sanitize_starts_with_digitasserts that123apiis the expected module name, encoding the bug. This test should instead assert the output is_123api(or similar) after the fix.@ -0,0 +384,4 @@}struct TempDir(std::path::PathBuf);[style]
generate()does not guard against an emptyspecslist. If the builder is misconfigured,write_top_modwrites an emptymod.rs. Add a check returningErrwhen specs is empty.[style] Exact string
use crate::{apis::ResponseContent, models};is hardcoded. If openapi-generator changes import formatting (different items, ordering, or multi-line), the replacement silently fails. Consider a more flexible pattern or AST-based rewrite.