use log::warn;
use schemars::schema::{Schema, SchemaObject};
use serde::Deserialize;
use crate::{type_entry::TypeEntry, CrateVers, Name, Result, TypeSpace};
const RUST_TYPE_EXTENSION: &str = "x-rust-type";
#[derive(Deserialize)]
struct RustExtension {
#[serde(rename = "crate")]
crate_name: String,
version: String,
path: String,
#[serde(default)]
parameters: Vec<Schema>,
}
impl TypeSpace {
pub(crate) fn convert_rust_extension(&mut self, schema: &SchemaObject) -> Option<TypeEntry> {
let x_rust = schema.extensions.get(RUST_TYPE_EXTENSION)?;
let Ok(RustExtension {
crate_name,
version,
path,
parameters,
}) = serde_json::from_value(x_rust.clone())
else {
warn!(
"{} contains an invalid value for {}",
serde_json::to_string_pretty(&schema).unwrap(),
RUST_TYPE_EXTENSION,
);
return None;
};
let Ok(req) = semver::VersionReq::parse(&version) else {
warn!(
"{} contains an invalid version",
serde_json::to_string_pretty(&schema).unwrap(),
);
return None;
};
let crate_ident = crate_name.replace('-', "_");
let path_sep = path.find("::")?;
if crate_ident != path[..path_sep] {
warn!(
"{} path doesn't start with crate name",
serde_json::to_string_pretty(&schema).unwrap(),
);
return None;
}
let path = {
if let Some(crate_spec) = self.settings.crates.get(crate_name.as_str()) {
match &crate_spec.version {
CrateVers::Any => (),
CrateVers::Version(version) if req.matches(version) => (),
_ => return None,
}
if let Some(new_crate) = &crate_spec.rename {
format!("{}{}", new_crate.replace('-', "_"), &path[path_sep..])
} else {
path
}
} else {
match self.settings.unknown_crates {
crate::UnknownPolicy::Generate => return None,
crate::UnknownPolicy::Allow => path,
crate::UnknownPolicy::Deny => return None,
}
}
};
let param_ids = parameters
.iter()
.map(|p_schema| {
let (param_id, _) = self.id_for_schema(Name::Unknown, p_schema)?;
Ok(param_id)
})
.collect::<Result<Vec<_>>>()
.ok()?;
Some(TypeEntry::new_native_params(
format!("::{path}"),
¶m_ids,
))
}
}