oxidecomputer / typify

JSON Schema -> Rust type converter
Apache License 2.0
367 stars 53 forks source link

`TypeSpaceSettings::with_replacement` only applies to types generated from `$ref`s #583

Open miwig opened 2 months ago

miwig commented 2 months ago

It would be nice to be able to replace types that are created "inline", e.g. for array items.

ahl commented 2 months ago

The replace logic may be a little haphazard--it could be that it would be more coherent to do in a pass after after processing the schema(s). Can you share the schema you're working with and perhaps show what you'd like the eventual output to look like? Thanks.

miwig commented 2 months ago

Sure.

{
  "title": "MyList",
  "type": "object",
  "properties": {
    "foo": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "bar": { "type": "string" }
        }
      }
    }
  }
}

processed with:

use std::fs::File;

use typify::{TypeSpace, TypeSpaceSettings};

fn main() -> anyhow::Result<()> {
    let mut type_space = TypeSpace::new(&TypeSpaceSettings::default().with_replacement(
        "MyListFooItem",
        "HandwrittenFoo",
        [].into_iter(),
    ));

    let f = File::open("test-rename.schema.json")?;
    let schema: schemars::schema::Schema = serde_json::from_reader(f)?;
    type_space.add_type(&schema)?;

    let contents = format!(
        "{}\n{}",
        "use serde::{Deserialize, Serialize};",
        prettyplease::unparse(&syn::parse2::<syn::File>(type_space.to_stream()).unwrap())
    );

    println!("{}", contents);

    Ok(())
}

produces

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MyList {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub foo: Vec<MyListFooItem>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MyListFooItem {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub bar: Option<String>,
}

I would want it to produce

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct MyList {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub foo: Vec<HandwrittenFoo>,
}

My current workaround is to either pre-process the schema as json first, replacing "items" with a $ref, or to just do string replacement on the generated source.