use std::{env, path::PathBuf};
#[cfg(feature = "derive")]
use std::{fs, path::Path};
#[cfg(feature = "derive")]
use crate::schema::Schema;
use crate::schema::error::{Result, SchemaError};
#[cfg(feature = "derive")]
use crate::{FromRon, ToRon};
pub const SCHEMA_DIR_ENV: &str = "RON_SCHEMA_DIR";
#[cfg(feature = "xdg")]
const DEFAULT_SCHEMA_DIR: &str = "ron-schemas";
const SCHEMA_EXTENSION: &str = "schema.ron";
pub fn resolve_schema_dir() -> Result<PathBuf> {
if let Ok(dir) = env::var(SCHEMA_DIR_ENV) {
return Ok(PathBuf::from(dir));
}
#[cfg(feature = "xdg")]
{
dirs::data_dir()
.map(|d| d.join(DEFAULT_SCHEMA_DIR))
.ok_or_else(SchemaError::no_schema_dir)
}
#[cfg(not(feature = "xdg"))]
{
Err(SchemaError::no_schema_dir())
}
}
#[must_use]
pub fn type_path_to_file_path(type_path: &str) -> PathBuf {
let parts: Vec<&str> = type_path.split("::").collect();
let mut path = PathBuf::new();
for (i, part) in parts.iter().enumerate() {
if i == parts.len() - 1 {
path.push(format!("{part}.{SCHEMA_EXTENSION}"));
} else {
path.push(part);
}
}
path
}
#[cfg(feature = "derive")]
pub fn write_schema(
type_path: &str,
schema: &Schema,
output_dir: Option<&Path>,
) -> Result<PathBuf> {
let base_dir = match output_dir {
Some(dir) => dir.to_path_buf(),
None => resolve_schema_dir()?,
};
let file_path = base_dir.join(type_path_to_file_path(type_path));
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)?;
}
let contents = schema.to_ron()?;
fs::write(&file_path, contents)?;
Ok(file_path)
}
#[cfg(feature = "derive")]
pub fn read_schema(path: &Path) -> Result<Schema> {
let contents = fs::read_to_string(path)?;
let schema = Schema::from_ron(&contents)?;
Ok(schema)
}
#[cfg(feature = "derive")]
pub fn find_schema(type_path: &str) -> Result<Schema> {
let base_dir = resolve_schema_dir()?;
let file_path = base_dir.join(type_path_to_file_path(type_path));
if !file_path.exists() {
return Err(SchemaError::schema_not_found(type_path));
}
read_schema(&file_path)
}
#[cfg(feature = "derive")]
pub fn find_schema_in(type_path: &str, search_dir: &Path) -> Result<Schema> {
let file_path = search_dir.join(type_path_to_file_path(type_path));
if file_path.exists() {
return read_schema(&file_path);
}
find_schema(type_path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_path_to_file_path() {
assert_eq!(
type_path_to_file_path("my_crate::config::AppConfig"),
PathBuf::from("my_crate/config/AppConfig.schema.ron")
);
assert_eq!(
type_path_to_file_path("MyType"),
PathBuf::from("MyType.schema.ron")
);
}
}