use std::collections::{BTreeMap, BTreeSet};
use oas3::spec::ObjectSchema;
use super::{ConversionResult, hashing};
use crate::{
generator::ast::RustType,
naming::{identifiers::to_rust_type_name, inference as naming},
};
pub(crate) struct SharedSchemaCache {
schema_to_type: BTreeMap<String, String>,
enum_to_type: BTreeMap<Vec<String>, String>,
generated_types: Vec<RustType>,
used_names: BTreeSet<String>,
precomputed_names: BTreeMap<String, String>,
precomputed_enum_names: BTreeMap<Vec<String>, String>,
}
impl SharedSchemaCache {
pub(crate) fn new() -> Self {
Self {
schema_to_type: BTreeMap::new(),
enum_to_type: BTreeMap::new(),
generated_types: Vec::new(),
used_names: BTreeSet::new(),
precomputed_names: BTreeMap::new(),
precomputed_enum_names: BTreeMap::new(),
}
}
pub(crate) fn set_precomputed_names(
&mut self,
names: BTreeMap<String, String>,
enum_names: BTreeMap<Vec<String>, String>,
) {
self.precomputed_names = names;
self.precomputed_enum_names = enum_names;
}
pub(crate) fn get_type_name(&self, schema: &ObjectSchema) -> ConversionResult<Option<String>> {
let schema_hash = hashing::hash_schema(schema)?;
Ok(self.schema_to_type.get(&schema_hash).cloned())
}
pub(crate) fn get_enum_name(&self, values: &[String]) -> Option<String> {
if let Some(name) = self.enum_to_type.get(values) {
Some(name.clone())
} else {
self.precomputed_enum_names.get(values).cloned()
}
}
pub(crate) fn is_enum_generated(&self, values: &[String]) -> bool {
self.enum_to_type.contains_key(values)
}
pub(crate) fn register_enum(&mut self, values: Vec<String>, name: String) {
self.enum_to_type.insert(values, name);
}
pub(crate) fn mark_name_used(&mut self, name: String) {
self.used_names.insert(name);
}
pub(crate) fn get_preferred_name(&self, schema: &ObjectSchema, base_name: &str) -> ConversionResult<String> {
let schema_hash = hashing::hash_schema(schema)?;
if let Some(name) = self.precomputed_names.get(&schema_hash) {
return Ok(name.clone());
}
Ok(self.make_unique_name(base_name))
}
pub(crate) fn register_type(
&mut self,
schema: &ObjectSchema,
base_name: &str,
mut nested_types: Vec<RustType>,
type_def: RustType,
) -> ConversionResult<String> {
let schema_hash = hashing::hash_schema(schema)?;
if !naming::is_relaxed_enum_pattern(schema)
&& let Some(values) = naming::extract_enum_values(schema)
&& let Some(existing_name) = self.enum_to_type.get(&values)
{
self.schema_to_type.insert(schema_hash, existing_name.clone());
return Ok(existing_name.clone());
}
let mut name = base_name.to_string();
if self.used_names.contains(&name) {
if let Some(existing_name) = self.schema_to_type.get(&schema_hash) {
return Ok(existing_name.clone());
}
name = self.make_unique_name(&name);
}
self.used_names.insert(name.clone());
self.schema_to_type.insert(schema_hash, name.clone());
if let Some(values) = naming::extract_enum_values(schema) {
self.enum_to_type.insert(values, name.clone());
}
let mut final_type_def = type_def;
match &mut final_type_def {
RustType::Struct(s) => s.name.clone_from(&name),
RustType::Enum(e) => e.name.clone_from(&name),
_ => {}
}
self.generated_types.append(&mut nested_types);
self.generated_types.push(final_type_def);
Ok(name)
}
pub(crate) fn make_unique_name(&self, base: &str) -> String {
let rust_name = to_rust_type_name(base);
naming::ensure_unique(&rust_name, &self.used_names)
}
pub(crate) fn into_types(self) -> Vec<RustType> {
self.generated_types
}
}