use geth_types::{DocumentId, ResourceId}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DocumentResource { pub id: DocumentId, pub resource: ResourceId, pub name: String, pub sync_status: String, pub state_bytes: u64, } #[derive(Debug, thiserror::Error)] pub enum DocumentError { #[error("invalid document name: {0}")] InvalidName(String), } pub fn validate_document_name(name: &str) -> Result<(), DocumentError> { if name.is_empty() || !name .bytes() .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-')) { return Err(DocumentError::InvalidName(name.to_owned())); } Ok(()) } #[must_use] pub fn automerge_roadmap() -> &'static str { "future documents use Automerge sync over Iroh with resource-local authorization" } #[cfg(test)] mod tests { use super::*; #[test] fn document_name_validation_rejects_paths_and_empty_names() { assert!(validate_document_name("notes").is_ok()); assert!(validate_document_name("notes.v1").is_ok()); assert!(validate_document_name("").is_err()); assert!(validate_document_name("../notes").is_err()); assert!(validate_document_name("notes/main").is_err()); assert!(validate_document_name("notes main").is_err()); } }