Allow conversion overrides by specifying a schema (#280)
This commit is contained in:
parent
df5f513083
commit
57031d77d3
|
@ -1158,6 +1158,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"progenitor-impl",
|
"progenitor-impl",
|
||||||
"quote",
|
"quote",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_tokenstream",
|
"serde_tokenstream",
|
||||||
|
@ -1928,7 +1929,7 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typify"
|
name = "typify"
|
||||||
version = "0.0.11-dev"
|
version = "0.0.11-dev"
|
||||||
source = "git+https://github.com/oxidecomputer/typify#1aefa41396d0aefa1a9dbb32320445ab02a5cbc3"
|
source = "git+https://github.com/oxidecomputer/typify#da0505b0b43180ee85d8df2a7c2504acb7ce01e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typify-impl",
|
"typify-impl",
|
||||||
"typify-macro",
|
"typify-macro",
|
||||||
|
@ -1937,7 +1938,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typify-impl"
|
name = "typify-impl"
|
||||||
version = "0.0.11-dev"
|
version = "0.0.11-dev"
|
||||||
source = "git+https://github.com/oxidecomputer/typify#1aefa41396d0aefa1a9dbb32320445ab02a5cbc3"
|
source = "git+https://github.com/oxidecomputer/typify#da0505b0b43180ee85d8df2a7c2504acb7ce01e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1955,7 +1956,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typify-macro"
|
name = "typify-macro"
|
||||||
version = "0.0.11-dev"
|
version = "0.0.11-dev"
|
||||||
source = "git+https://github.com/oxidecomputer/typify#1aefa41396d0aefa1a9dbb32320445ab02a5cbc3"
|
source = "git+https://github.com/oxidecomputer/typify#da0505b0b43180ee85d8df2a7c2504acb7ce01e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -15,5 +15,6 @@ members = [
|
||||||
#typify = { path = "../typify/typify" }
|
#typify = { path = "../typify/typify" }
|
||||||
|
|
||||||
#[patch.crates-io]
|
#[patch.crates-io]
|
||||||
|
#serde_tokenstream = { path = "../serde_tokenstream" }
|
||||||
#typify = { path = "../typify/typify" }
|
#typify = { path = "../typify/typify" }
|
||||||
#rustfmt-wrapper = { path = "../rustfmt-wrapper" }
|
#rustfmt-wrapper = { path = "../rustfmt-wrapper" }
|
||||||
|
|
|
@ -3,8 +3,9 @@ name = "progenitor-impl"
|
||||||
version = "0.2.1-dev"
|
version = "0.2.1-dev"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
repository = "https://github.com/oxidecomputer/progenitor.git"
|
|
||||||
description = "An OpenAPI client generator - core implementation"
|
description = "An OpenAPI client generator - core implementation"
|
||||||
|
repository = "https://github.com/oxidecomputer/progenitor.git"
|
||||||
|
readme = "../README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
heck = "0.4.0"
|
heck = "0.4.0"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pre-release-replacements = []
|
|
@ -52,6 +52,7 @@ pub struct GenerationSettings {
|
||||||
post_hook: Option<TokenStream>,
|
post_hook: Option<TokenStream>,
|
||||||
extra_derives: Vec<String>,
|
extra_derives: Vec<String>,
|
||||||
patch: HashMap<String, TypePatch>,
|
patch: HashMap<String, TypePatch>,
|
||||||
|
convert: Vec<(schemars::schema::SchemaObject, String, Vec<String>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -122,6 +123,20 @@ impl GenerationSettings {
|
||||||
.insert(type_name.as_ref().to_string(), patch.clone());
|
.insert(type_name.as_ref().to_string(), patch.clone());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_conversion<S: ToString, I: Iterator<Item = impl ToString>>(
|
||||||
|
&mut self,
|
||||||
|
schema: schemars::schema::SchemaObject,
|
||||||
|
type_name: S,
|
||||||
|
impls: I,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.convert.push((
|
||||||
|
schema,
|
||||||
|
type_name.to_string(),
|
||||||
|
impls.map(|x| x.to_string()).collect(),
|
||||||
|
));
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Generator {
|
impl Default for Generator {
|
||||||
|
@ -149,6 +164,16 @@ impl Generator {
|
||||||
settings.patch.iter().for_each(|(type_name, patch)| {
|
settings.patch.iter().for_each(|(type_name, patch)| {
|
||||||
type_settings.with_patch(type_name, patch);
|
type_settings.with_patch(type_name, patch);
|
||||||
});
|
});
|
||||||
|
settings
|
||||||
|
.convert
|
||||||
|
.iter()
|
||||||
|
.for_each(|(schema, type_name, impls)| {
|
||||||
|
type_settings.with_conversion(
|
||||||
|
schema.clone(),
|
||||||
|
type_name,
|
||||||
|
impls.iter(),
|
||||||
|
);
|
||||||
|
});
|
||||||
Self {
|
Self {
|
||||||
type_space: TypeSpace::new(&type_settings),
|
type_space: TypeSpace::new(&type_settings),
|
||||||
settings: settings.clone(),
|
settings: settings.clone(),
|
||||||
|
|
|
@ -55,9 +55,9 @@ pub mod types {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
|
||||||
pub struct ReportFinishBody {
|
pub struct ReportFinishBody {
|
||||||
pub duration_millis: i32,
|
pub duration_millis: usize,
|
||||||
pub end_time: chrono::DateTime<chrono::offset::Utc>,
|
pub end_time: chrono::DateTime<chrono::offset::Utc>,
|
||||||
pub exit_status: i32,
|
pub exit_status: usize,
|
||||||
pub id: ReportId,
|
pub id: ReportId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +120,11 @@ pub mod types {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
|
||||||
pub struct ReportSummary {
|
pub struct ReportSummary {
|
||||||
pub age_seconds: i32,
|
pub age_seconds: usize,
|
||||||
pub duration_seconds: i32,
|
pub duration_seconds: usize,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub job: String,
|
pub job: String,
|
||||||
pub status: i32,
|
pub status: usize,
|
||||||
pub when: chrono::DateTime<chrono::offset::Utc>,
|
pub when: chrono::DateTime<chrono::offset::Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +324,9 @@ pub mod types {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReportFinishBody {
|
pub struct ReportFinishBody {
|
||||||
duration_millis: Result<i32, String>,
|
duration_millis: Result<usize, String>,
|
||||||
end_time: Result<chrono::DateTime<chrono::offset::Utc>, String>,
|
end_time: Result<chrono::DateTime<chrono::offset::Utc>, String>,
|
||||||
exit_status: Result<i32, String>,
|
exit_status: Result<usize, String>,
|
||||||
id: Result<super::ReportId, String>,
|
id: Result<super::ReportId, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ pub mod types {
|
||||||
impl ReportFinishBody {
|
impl ReportFinishBody {
|
||||||
pub fn duration_millis<T>(mut self, value: T) -> Self
|
pub fn duration_millis<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: std::convert::TryInto<i32>,
|
T: std::convert::TryInto<usize>,
|
||||||
T::Error: std::fmt::Display,
|
T::Error: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.duration_millis = value.try_into().map_err(|e| {
|
self.duration_millis = value.try_into().map_err(|e| {
|
||||||
|
@ -364,7 +364,7 @@ pub mod types {
|
||||||
}
|
}
|
||||||
pub fn exit_status<T>(mut self, value: T) -> Self
|
pub fn exit_status<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: std::convert::TryInto<i32>,
|
T: std::convert::TryInto<usize>,
|
||||||
T::Error: std::fmt::Display,
|
T::Error: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.exit_status = value
|
self.exit_status = value
|
||||||
|
@ -624,11 +624,11 @@ pub mod types {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReportSummary {
|
pub struct ReportSummary {
|
||||||
age_seconds: Result<i32, String>,
|
age_seconds: Result<usize, String>,
|
||||||
duration_seconds: Result<i32, String>,
|
duration_seconds: Result<usize, String>,
|
||||||
host: Result<String, String>,
|
host: Result<String, String>,
|
||||||
job: Result<String, String>,
|
job: Result<String, String>,
|
||||||
status: Result<i32, String>,
|
status: Result<usize, String>,
|
||||||
when: Result<chrono::DateTime<chrono::offset::Utc>, String>,
|
when: Result<chrono::DateTime<chrono::offset::Utc>, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +648,7 @@ pub mod types {
|
||||||
impl ReportSummary {
|
impl ReportSummary {
|
||||||
pub fn age_seconds<T>(mut self, value: T) -> Self
|
pub fn age_seconds<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: std::convert::TryInto<i32>,
|
T: std::convert::TryInto<usize>,
|
||||||
T::Error: std::fmt::Display,
|
T::Error: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.age_seconds = value
|
self.age_seconds = value
|
||||||
|
@ -658,7 +658,7 @@ pub mod types {
|
||||||
}
|
}
|
||||||
pub fn duration_seconds<T>(mut self, value: T) -> Self
|
pub fn duration_seconds<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: std::convert::TryInto<i32>,
|
T: std::convert::TryInto<usize>,
|
||||||
T::Error: std::fmt::Display,
|
T::Error: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.duration_seconds = value.try_into().map_err(|e| {
|
self.duration_seconds = value.try_into().map_err(|e| {
|
||||||
|
@ -691,7 +691,7 @@ pub mod types {
|
||||||
}
|
}
|
||||||
pub fn status<T>(mut self, value: T) -> Self
|
pub fn status<T>(mut self, value: T) -> Self
|
||||||
where
|
where
|
||||||
T: std::convert::TryInto<i32>,
|
T: std::convert::TryInto<usize>,
|
||||||
T::Error: std::fmt::Display,
|
T::Error: std::fmt::Display,
|
||||||
{
|
{
|
||||||
self.status = value
|
self.status = value
|
||||||
|
|
|
@ -26,7 +26,18 @@ fn verify_apis(openapi_file: &str) {
|
||||||
.with_interface(InterfaceStyle::Builder)
|
.with_interface(InterfaceStyle::Builder)
|
||||||
.with_tag(TagStyle::Merged)
|
.with_tag(TagStyle::Merged)
|
||||||
.with_derive("JsonSchema")
|
.with_derive("JsonSchema")
|
||||||
.with_patch("Name", TypePatch::default().with_derive("Hash")),
|
.with_patch("Name", TypePatch::default().with_derive("Hash"))
|
||||||
|
.with_conversion(
|
||||||
|
schemars::schema::SchemaObject {
|
||||||
|
instance_type: Some(
|
||||||
|
schemars::schema::InstanceType::Integer.into(),
|
||||||
|
),
|
||||||
|
format: Some("int32".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
"usize",
|
||||||
|
["Display"].into_iter(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||||
expectorate::assert_contents(
|
expectorate::assert_contents(
|
||||||
|
|
|
@ -3,18 +3,20 @@ name = "progenitor-macro"
|
||||||
version = "0.2.1-dev"
|
version = "0.2.1-dev"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
repository = "https://github.com/oxidecomputer/progenitor.git"
|
|
||||||
description = "An OpenAPI client generator - macros"
|
description = "An OpenAPI client generator - macros"
|
||||||
|
repository = "https://github.com/oxidecomputer/progenitor.git"
|
||||||
|
readme = "../README.md"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
openapiv3 = "1.0.0"
|
openapiv3 = "1.0.0"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
progenitor-impl = { version = "0.2.1-dev", path = "../progenitor-impl" }
|
progenitor-impl = { version = "0.2.1-dev", path = "../progenitor-impl" }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
schemars = "0.8.11"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_tokenstream = "0.1.6"
|
serde_tokenstream = "0.1.6"
|
||||||
syn = "1.0"
|
syn = "1.0"
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pre-release-replacements = []
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2022 Oxide Computer Company
|
// Copyright 2022 Oxide Computer Company
|
||||||
|
|
||||||
use std::{collections::HashMap, path::Path};
|
use std::{collections::HashMap, fmt::Display, path::Path};
|
||||||
|
|
||||||
use openapiv3::OpenAPI;
|
use openapiv3::OpenAPI;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
@ -8,8 +8,9 @@ use progenitor_impl::{
|
||||||
GenerationSettings, Generator, InterfaceStyle, TagStyle, TypePatch,
|
GenerationSettings, Generator, InterfaceStyle, TagStyle, TypePatch,
|
||||||
};
|
};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
use schemars::schema::SchemaObject;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_tokenstream::ParseWrapper;
|
use serde_tokenstream::{OrderedMap, ParseWrapper};
|
||||||
use syn::LitStr;
|
use syn::LitStr;
|
||||||
|
|
||||||
/// Generates a client from the given OpenAPI document
|
/// Generates a client from the given OpenAPI document
|
||||||
|
@ -82,6 +83,24 @@ struct MacroSettings {
|
||||||
derives: Vec<ParseWrapper<syn::Path>>,
|
derives: Vec<ParseWrapper<syn::Path>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
patch: HashMap<ParseWrapper<syn::Type>, MacroPatch>,
|
patch: HashMap<ParseWrapper<syn::Type>, MacroPatch>,
|
||||||
|
#[serde(default)]
|
||||||
|
convert: OrderedMap<
|
||||||
|
SchemaObject,
|
||||||
|
(ParseWrapper<syn::Path>, Vec<MacroSettingsImpl>),
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
enum MacroSettingsImpl {
|
||||||
|
Display,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MacroSettingsImpl {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MacroSettingsImpl::Display => f.write_str("Display"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -153,6 +172,7 @@ fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
|
||||||
post_hook,
|
post_hook,
|
||||||
derives,
|
derives,
|
||||||
patch,
|
patch,
|
||||||
|
convert,
|
||||||
} = serde_tokenstream::from_tokenstream(&item.into())?;
|
} = serde_tokenstream::from_tokenstream(&item.into())?;
|
||||||
let mut settings = GenerationSettings::default();
|
let mut settings = GenerationSettings::default();
|
||||||
settings.with_interface(interface);
|
settings.with_interface(interface);
|
||||||
|
@ -174,6 +194,15 @@ fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
|
||||||
&patch.into(),
|
&patch.into(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
convert
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|(schema, (type_name, impls))| {
|
||||||
|
settings.with_conversion(
|
||||||
|
schema,
|
||||||
|
type_name.to_token_stream(),
|
||||||
|
impls.into_iter(),
|
||||||
|
);
|
||||||
|
});
|
||||||
(spec.into_inner(), settings)
|
(spec.into_inner(), settings)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@ name = "progenitor"
|
||||||
version = "0.2.1-dev"
|
version = "0.2.1-dev"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
repository = "https://github.com/oxidecomputer/progenitor.git"
|
|
||||||
description = "An OpenAPI client generator"
|
description = "An OpenAPI client generator"
|
||||||
|
repository = "https://github.com/oxidecomputer/progenitor.git"
|
||||||
|
readme = "../README.md"
|
||||||
|
keywords = ["openapi", "openapiv3", "sdk", "generator", "proc_macro"]
|
||||||
|
categories = ["api-bindings", "compilers"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
progenitor-client = { version = "0.2.1-dev", path = "../progenitor-client" }
|
progenitor-client = { version = "0.2.1-dev", path = "../progenitor-client" }
|
||||||
|
|
Loading…
Reference in New Issue