use alloc::collections::{BTreeMap, VecDeque};
use std::collections::HashSet;
#[cfg(feature = "derive")]
use std::path::{Path, PathBuf};
#[cfg(feature = "derive")]
use crate::schema::write_schema;
use crate::schema::{RonSchema, Schema, SchemaError, error::StorageError};
#[derive(Clone, Copy, Debug)]
pub struct SchemaEntry {
pub type_path: &'static str,
pub schema: fn() -> Schema,
pub children: fn() -> &'static [&'static SchemaEntry],
}
#[derive(Clone, Debug, Default)]
pub struct SchemaCatalog {
pub schemas: BTreeMap<String, Schema>,
}
impl SchemaCatalog {
#[cfg(feature = "derive")]
pub fn write_all(&self, output_dir: Option<&Path>) -> Result<Vec<PathBuf>, SchemaError> {
let mut written = Vec::with_capacity(self.schemas.len());
for (type_path, schema) in &self.schemas {
written.push(write_schema(type_path, schema, output_dir)?);
}
Ok(written)
}
}
pub fn collect_schemas<T: RonSchema>() -> Result<SchemaCatalog, SchemaError> {
let type_path = T::type_path().ok_or_else(|| {
SchemaError::Storage(StorageError::Io(
"type does not support schema storage".to_string(),
))
})?;
let root = SchemaEntry {
type_path,
schema: T::schema,
children: T::child_schemas,
};
Ok(collect_from_root(&root))
}
#[cfg(feature = "derive")]
pub fn write_schemas<T: RonSchema>(output_dir: Option<&str>) -> Result<Vec<PathBuf>, SchemaError> {
let catalog = collect_schemas::<T>()?;
let output_path = output_dir.map(Path::new);
catalog.write_all(output_path)
}
fn collect_from_root(root: &SchemaEntry) -> SchemaCatalog {
let mut catalog = SchemaCatalog::default();
let mut queue = VecDeque::new();
let mut seen = HashSet::new();
queue.push_back(root);
while let Some(entry) = queue.pop_front() {
if !seen.insert(entry.type_path) {
continue;
}
let schema = (entry.schema)();
catalog.schemas.insert(entry.type_path.to_string(), schema);
for child in (entry.children)() {
queue.push_back(child);
}
}
catalog
}