use crate::{Document, Reference};
pub const DEFAULT_SCHEMA: &[&str] = &["id", "value"];
pub trait ImportConfig {
fn default_type_name(&self) -> &str;
fn version(&self) -> (u32, u32);
}
pub trait ExportConfig {
fn include_metadata(&self) -> bool;
fn pretty(&self) -> bool;
}
pub trait FromFormat: Sized {
type Config: ImportConfig;
type Error;
fn from_format(input: &str, config: &Self::Config) -> Result<Document, Self::Error>;
}
pub trait ToFormat {
type Config: ExportConfig;
type Error;
fn to_format(doc: &Document, config: &Self::Config) -> Result<String, Self::Error>;
}
pub fn parse_reference(s: &str) -> Result<Reference, String> {
if let Some(stripped) = s.strip_prefix('@') {
if let Some((type_name, id)) = stripped.split_once(':') {
Ok(Reference::qualified(type_name, id))
} else {
Ok(Reference::local(stripped))
}
} else {
Err(format!(
"Invalid reference format: expected '@' prefix, got '{}'",
s
))
}
}
#[derive(Debug, Clone)]
pub struct BaseImportConfig {
pub default_type_name: String,
pub version: (u32, u32),
}
impl Default for BaseImportConfig {
fn default() -> Self {
Self {
default_type_name: "Item".to_string(),
version: (1, 0),
}
}
}
impl ImportConfig for BaseImportConfig {
fn default_type_name(&self) -> &str {
&self.default_type_name
}
fn version(&self) -> (u32, u32) {
self.version
}
}
#[derive(Debug, Clone)]
pub struct BaseExportConfig {
pub include_metadata: bool,
pub pretty: bool,
pub indent: String,
}
impl Default for BaseExportConfig {
fn default() -> Self {
Self {
include_metadata: true,
pretty: true,
indent: " ".to_string(),
}
}
}
impl ExportConfig for BaseExportConfig {
fn include_metadata(&self) -> bool {
self.include_metadata
}
fn pretty(&self) -> bool {
self.pretty
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_reference_local() {
let r = parse_reference("@user123").unwrap();
assert_eq!(r.type_name, None);
assert_eq!(r.id.as_ref(), "user123");
}
#[test]
fn test_parse_reference_qualified() {
let r = parse_reference("@User:123").unwrap();
assert_eq!(r.type_name.as_deref(), Some("User"));
assert_eq!(r.id.as_ref(), "123");
}
#[test]
fn test_parse_reference_with_dashes() {
let r = parse_reference("@my-item_123").unwrap();
assert_eq!(r.type_name, None);
assert_eq!(r.id.as_ref(), "my-item_123");
}
#[test]
fn test_parse_reference_qualified_with_dashes() {
let r = parse_reference("@My-Type:item-123").unwrap();
assert_eq!(r.type_name.as_deref(), Some("My-Type"));
assert_eq!(r.id.as_ref(), "item-123");
}
#[test]
fn test_parse_reference_invalid_no_at() {
let result = parse_reference("user123");
assert!(result.is_err());
assert!(result.unwrap_err().contains("Invalid reference format"));
}
#[test]
fn test_parse_reference_empty_after_at() {
let r = parse_reference("@").unwrap();
assert_eq!(r.type_name, None);
assert_eq!(r.id.as_ref(), "");
}
#[test]
fn test_parse_reference_empty_type_and_id() {
let r = parse_reference("@:").unwrap();
assert_eq!(r.type_name.as_deref(), Some(""));
assert_eq!(r.id.as_ref(), "");
}
#[test]
fn test_base_import_config_default() {
let config = BaseImportConfig::default();
assert_eq!(config.default_type_name, "Item");
assert_eq!(config.version, (1, 0));
}
#[test]
fn test_base_import_config_trait() {
let config = BaseImportConfig::default();
assert_eq!(config.default_type_name(), "Item");
assert_eq!(config.version(), (1, 0));
}
#[test]
fn test_base_import_config_custom() {
let config = BaseImportConfig {
default_type_name: "Custom".to_string(),
version: (2, 5),
};
assert_eq!(config.default_type_name(), "Custom");
assert_eq!(config.version(), (2, 5));
}
#[test]
fn test_base_import_config_clone() {
let config = BaseImportConfig::default();
let cloned = config.clone();
assert_eq!(config.default_type_name, cloned.default_type_name);
assert_eq!(config.version, cloned.version);
}
#[test]
fn test_base_export_config_default() {
let config = BaseExportConfig::default();
assert!(config.include_metadata);
assert!(config.pretty);
assert_eq!(config.indent, " ");
}
#[test]
fn test_base_export_config_trait() {
let config = BaseExportConfig::default();
assert!(config.include_metadata());
assert!(config.pretty());
}
#[test]
fn test_base_export_config_custom() {
let config = BaseExportConfig {
include_metadata: false,
pretty: false,
indent: "\t".to_string(),
};
assert!(!config.include_metadata());
assert!(!config.pretty());
assert_eq!(config.indent, "\t");
}
#[test]
fn test_base_export_config_clone() {
let config = BaseExportConfig::default();
let cloned = config.clone();
assert_eq!(config.include_metadata, cloned.include_metadata);
assert_eq!(config.pretty, cloned.pretty);
assert_eq!(config.indent, cloned.indent);
}
#[test]
fn test_default_schema() {
assert_eq!(DEFAULT_SCHEMA, &["id", "value"]);
assert_eq!(DEFAULT_SCHEMA.len(), 2);
}
}