Skip to content

Commit eece263

Browse files
committed
Merge remote-tracking branch 'origin/master' into feature/add_custom_use_and_derive
2 parents dc0bf81 + b7c58fc commit eece263

File tree

18 files changed

+102
-38
lines changed

18 files changed

+102
-38
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44

55
jobs:
66
release_binary:
7+
permissions: write-all
78
runs-on: ${{ matrix.runs_on }}
89
strategy:
910
matrix:

crates/core/src/codegen/mod.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::target::{
99
use ast::{Ast, SchemaAst};
1010
use jtd::Schema;
1111
use namespace::Namespace;
12-
use std::collections::BTreeMap;
12+
use std::collections::{BTreeMap, BTreeSet};
1313
use std::fs::File;
1414
use std::io::Write;
1515
use std::path::Path;
@@ -36,6 +36,7 @@ struct CodeGenerator<'a, T> {
3636
out_dir: &'a Path,
3737
strategy: Strategy,
3838
definition_names: BTreeMap<String, String>,
39+
recursive_definitions: BTreeSet<String>,
3940
}
4041

4142
struct FileData<T> {
@@ -50,6 +51,7 @@ impl<'a, T: Target> CodeGenerator<'a, T> {
5051
out_dir,
5152
strategy: target.strategy(),
5253
definition_names: BTreeMap::new(),
54+
recursive_definitions: BTreeSet::new(),
5355
}
5456
}
5557

@@ -68,6 +70,18 @@ impl<'a, T: Target> CodeGenerator<'a, T> {
6870
self.definition_names.insert(name.clone(), ast_name);
6971
}
7072

73+
// Detect recursive definitions, as some target language need to handle
74+
// them specifically (e.g. Rust).
75+
// Note that a type is *not* considered to be recursive it it contains
76+
// instances of itself only through "elements" or "values"
77+
// (the underlying container is considered to break the recursion).
78+
for (name, ast) in &schema_ast.definitions {
79+
let mut visited = vec![];
80+
if find_recursion(name, ast, &schema_ast.definitions, &mut visited) {
81+
self.recursive_definitions.insert(name.clone());
82+
}
83+
}
84+
7185
// If the target is using FilePerType partitioning, then this state
7286
// won't actually be used at all. If it's using SingleFile partitioning,
7387
// then this is the only file state that will be used.
@@ -120,8 +134,17 @@ impl<'a, T: Target> CodeGenerator<'a, T> {
120134
// Ref nodes are a special sort of "expr-like" node, where we
121135
// already know what the name of the expression is; it's the name of
122136
// the definition.
123-
Ast::Ref { definition, .. } => self.definition_names[&definition].clone(),
124-
137+
// Note however that recursive definition may need some special
138+
// treatment by the target.
139+
Ast::Ref { metadata, definition } => {
140+
let sub_expr = self.definition_names[&definition].clone();
141+
if self.recursive_definitions.iter().any(|i| i == &definition) {
142+
self.target
143+
.expr(&mut file_data.state, metadata, Expr::RecursiveRef(sub_expr))
144+
} else {
145+
sub_expr
146+
}
147+
}
125148
// The remaining "expr-like" node types just build up strings and
126149
// possibly alter the per-file state (usually in order to add
127150
// "imports" to the file).
@@ -479,3 +502,33 @@ impl<'a, T: Target> CodeGenerator<'a, T> {
479502
Ok(())
480503
}
481504
}
505+
506+
fn find_recursion(name: &str, ast: &Ast, definitions: &BTreeMap<String, Ast>, visited: &mut Vec<String>) -> bool {
507+
match ast {
508+
Ast::Ref { definition, .. } => {
509+
if definition == name {
510+
true
511+
} else if visited.iter().any(|i| i == &name) {
512+
false
513+
} else if let Some(ast2) = definitions.get(definition) {
514+
visited.push(definition.clone());
515+
find_recursion(name, &ast2, definitions, visited)
516+
} else {
517+
false
518+
}
519+
}
520+
Ast::NullableOf { type_, .. } => {
521+
find_recursion(name, type_, definitions, visited)
522+
}
523+
Ast::Struct { fields, .. } => {
524+
fields.iter()
525+
.any(|f| find_recursion(name, &f.type_, definitions, visited))
526+
}
527+
Ast::Discriminator { variants, .. } => {
528+
variants.iter()
529+
.flat_map(|v| v.fields.iter())
530+
.any(|f| find_recursion(name, &f.type_, definitions, visited))
531+
}
532+
_ => false,
533+
}
534+
}

crates/core/src/target/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub enum Expr {
8787
ArrayOf(String),
8888
DictOf(String),
8989
NullableOf(String),
90+
RecursiveRef(String),
9091
}
9192

9293
#[derive(Debug)]

crates/target_csharp_system_text/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ impl jtd_codegen::target::Target for Target {
125125
format!("IDictionary<string, {}>", sub_expr)
126126
}
127127
target::Expr::NullableOf(sub_expr) => format!("{}?", sub_expr),
128+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
128129
}
129130
}
130131

crates/target_go/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl jtd_codegen::target::Target for Target {
114114
target::Expr::ArrayOf(sub_expr) => format!("[]{}", sub_expr),
115115
target::Expr::DictOf(sub_expr) => format!("map[string]{}", sub_expr),
116116
target::Expr::NullableOf(sub_expr) => format!("*{}", sub_expr),
117+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
117118
}
118119
}
119120

crates/target_java_jackson/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ impl jtd_codegen::target::Target for Target {
125125
format!("Map<String, {}>", sub_expr)
126126
}
127127
target::Expr::NullableOf(sub_expr) => sub_expr, // everything is already nullable
128+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
128129
}
129130
}
130131

crates/target_python/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ impl jtd_codegen::target::Target for Target {
135135

136136
format!("Optional[{}]", sub_expr)
137137
}
138+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
138139
}
139140
}
140141

crates/target_ruby/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ impl jtd_codegen::target::Target for Target {
117117
format!("Hash[String, {}]", sub_expr)
118118
}
119119
target::Expr::NullableOf(sub_expr) => sub_expr,
120+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
120121
}
121122
}
122123

crates/target_ruby_sig/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ impl jtd_codegen::target::Target for Target {
116116
format!("Hash[String, {}]", sub_expr)
117117
}
118118
target::Expr::NullableOf(sub_expr) => format!("{}?", sub_expr),
119+
target::Expr::RecursiveRef(sub_expr) => sub_expr,
119120
}
120121
}
121122

crates/target_rust/output/custom_overrides/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ pub struct RootOverrideTypeDiscriminatorBaz {}
99
#[derive(Serialize, Deserialize)]
1010
pub struct Root {
1111
#[serde(rename = "override_elements_container")]
12-
pub overrideElementsContainer: Vec<String>,
12+
pub override_elements_container: Vec<String>,
1313

1414
#[serde(rename = "override_type_discriminator")]
15-
pub overrideTypeDiscriminator: serde_json::Value,
15+
pub override_type_discriminator: serde_json::Value,
1616

1717
#[serde(rename = "override_type_enum")]
18-
pub overrideTypeEnum: serde_json::Value,
18+
pub override_type_enum: serde_json::Value,
1919

2020
#[serde(rename = "override_type_expr")]
21-
pub overrideTypeExpr: serde_json::Value,
21+
pub override_type_expr: serde_json::Value,
2222

2323
#[serde(rename = "override_type_properties")]
24-
pub overrideTypeProperties: serde_json::Value,
24+
pub override_type_properties: serde_json::Value,
2525

2626
#[serde(rename = "override_values_container")]
27-
pub overrideValuesContainer: HashMap<String, String>,
27+
pub override_values_container: HashMap<String, String>,
2828
}

0 commit comments

Comments
 (0)