diff --git a/json_typegen_shared/src/generation/json_schema.rs b/json_typegen_shared/src/generation/json_schema.rs index 274efa3..dc4bc10 100644 --- a/json_typegen_shared/src/generation/json_schema.rs +++ b/json_typegen_shared/src/generation/json_schema.rs @@ -53,6 +53,7 @@ fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> Value { MapT { val_type: v } => generate_map_type(ctxt, path, v), Opaque(t) => Value::Object(string_hashmap! { "type" => Value::String(t.clone()) }), Optional(e) => type_from_shape(ctxt, path, e), + Nullable(e) => type_from_shape(ctxt, path, e), } } diff --git a/json_typegen_shared/src/generation/kotlin.rs b/json_typegen_shared/src/generation/kotlin.rs index 54a8a26..d87b022 100644 --- a/json_typegen_shared/src/generation/kotlin.rs +++ b/json_typegen_shared/src/generation/kotlin.rs @@ -80,6 +80,14 @@ fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option } else { (format!("{}?", inner), defs) } + }, + Nullable(e) => { + let (inner, defs) = type_from_shape(ctxt, path, e); + if ctxt.options.use_default_for_missing_fields { + (inner, defs) + } else { + (format!("{}?", inner), defs) + } } } } diff --git a/json_typegen_shared/src/generation/python.rs b/json_typegen_shared/src/generation/python.rs index 675b16c..a9fe78b 100644 --- a/json_typegen_shared/src/generation/python.rs +++ b/json_typegen_shared/src/generation/python.rs @@ -126,6 +126,15 @@ fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option let optional = import(ctxt, Import::Optional); (format!("{}[{}]", optional, inner), defs) } + }, + Nullable(e) => { + let (inner, defs) = type_from_shape(ctxt, path, e); + if ctxt.options.use_default_for_missing_fields { + (inner, defs) + } else { + let optional = import(ctxt, Import::Optional); + (format!("{}[{}]", optional, inner), defs) + } } } } diff --git a/json_typegen_shared/src/generation/rust.rs b/json_typegen_shared/src/generation/rust.rs index cf274aa..3715384 100644 --- a/json_typegen_shared/src/generation/rust.rs +++ b/json_typegen_shared/src/generation/rust.rs @@ -94,7 +94,15 @@ fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option } else { (format!("Option<{}>", inner), defs) } - } + }, + Nullable(e) => { + let (inner, defs) = type_from_shape(ctxt, path, e); + if ctxt.options.use_default_for_missing_fields { + (inner, defs) + } else { + (format!("Option<{}>", inner), defs) + } + }, } } diff --git a/json_typegen_shared/src/generation/shape.rs b/json_typegen_shared/src/generation/shape.rs index 08037b3..f090f67 100644 --- a/json_typegen_shared/src/generation/shape.rs +++ b/json_typegen_shared/src/generation/shape.rs @@ -46,6 +46,10 @@ fn type_from_shape(ctxt: &mut Ctxt, shape: &Shape) -> Value { "__type__" => Value::Str("optional"), "item" => type_from_shape(ctxt, e), }), + Nullable(e) => Value::Object(string_hashmap! { + "__type__" => Value::Str("nullable"), + "item" => type_from_shape(ctxt, e), + }), } } diff --git a/json_typegen_shared/src/generation/typescript.rs b/json_typegen_shared/src/generation/typescript.rs index b10ec3c..cfbc84f 100644 --- a/json_typegen_shared/src/generation/typescript.rs +++ b/json_typegen_shared/src/generation/typescript.rs @@ -64,6 +64,10 @@ fn type_from_shape(ctxt: &mut Ctxt, path: &str, shape: &Shape) -> (Ident, Option } else { (format!("{} | undefined", inner), defs) } + }, + Nullable(e) => { + let (inner, defs) = type_from_shape(ctxt, path, e); + (format!("{} | null", inner), defs) } } } diff --git a/json_typegen_shared/src/generation/typescript_type_alias.rs b/json_typegen_shared/src/generation/typescript_type_alias.rs index c4fa488..b0f184c 100644 --- a/json_typegen_shared/src/generation/typescript_type_alias.rs +++ b/json_typegen_shared/src/generation/typescript_type_alias.rs @@ -49,7 +49,15 @@ fn type_from_shape(ctxt: &mut Ctxt, shape: &Shape) -> Code { } else { format!("{} | undefined", inner) } - } + }, + Nullable(e) => { + let inner = type_from_shape(ctxt, e); + if ctxt.options.use_default_for_missing_fields { + inner + } else { + format!("{} | null", inner) + } + }, } } diff --git a/json_typegen_shared/src/shape.rs b/json_typegen_shared/src/shape.rs index 08ad9ff..d804798 100644 --- a/json_typegen_shared/src/shape.rs +++ b/json_typegen_shared/src/shape.rs @@ -15,9 +15,10 @@ pub enum Shape { /// represented by any single shape Any, - /// `Optional(T)` represents that a value is nullable, or not always present + /// `Optional(T)` represents that a value is not always present Optional(Box), - + /// `Nullable(T)` represents that a value is nullable + Nullable(Box), /// Equivalent to `Optional(Bottom)`, `Null` represents optionality with no further information Null, @@ -50,7 +51,7 @@ pub fn common_shape(a: Shape, b: Shape) -> Shape { match (a, b) { (a, Bottom) | (Bottom, a) => a, (Integer, Floating) | (Floating, Integer) => Floating, - (a, Null) | (Null, a) => a.into_optional(), + (a, Null) | (Null, a) => a.into_nullable(), (a, Optional(b)) | (Optional(b), a) => common_shape(a, *b).into_optional(), (Tuple(shapes1, n1), Tuple(shapes2, n2)) => { if shapes1.len() == shapes2.len() { @@ -81,6 +82,7 @@ pub fn common_shape(a: Shape, b: Shape) -> Shape { fields: common_field_shapes(f1, f2), }, (Opaque(t), _) | (_, Opaque(t)) => Opaque(t), + (a, Nullable(b)) | (Nullable(b), a) => common_shape(a, *b).into_nullable(), _ => Any, } } @@ -113,10 +115,19 @@ impl Shape { fn into_optional(self) -> Self { use self::Shape::*; match self { - Null | Any | Bottom | Optional(_) => self, + Null => Nullable(Box::new(self)), + Any | Bottom | Optional(_) => self, non_nullable => Optional(Box::new(non_nullable)), } } + fn into_nullable(self) -> Self { + use self::Shape::*; + match self { + Null => Nullable(Box::new(self)), + Any | Bottom | Nullable(_) => self, + non_nullable => Nullable(Box::new(non_nullable)), + } + } /// Note: This is asymmetrical because we don't unify based on this, /// but check if `self` can be used *as is* as a replacement for `other`