mollendorff_ref/
schema.rs1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ReferencesFile {
10 pub meta: Meta,
11 pub references: Vec<Reference>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Meta {
17 pub created: String,
19 pub last_verified: Option<String>,
21 pub tool: String,
23 pub total_links: usize,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Reference {
30 pub url: String,
32 pub title: String,
34 pub categories: Vec<String>,
36 pub cited_in: Vec<String>,
38 pub status: Status,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub verified: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub notes: Option<String>,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "lowercase")]
51pub enum Status {
52 Pending,
54 Ok,
56 Dead,
58 Redirect,
60 Paywall,
62 Login,
64}
65
66impl std::fmt::Display for Status {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 Status::Pending => write!(f, "pending"),
70 Status::Ok => write!(f, "ok"),
71 Status::Dead => write!(f, "dead"),
72 Status::Redirect => write!(f, "redirect"),
73 Status::Paywall => write!(f, "paywall"),
74 Status::Login => write!(f, "login"),
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_status_display() {
85 assert_eq!(Status::Pending.to_string(), "pending");
86 assert_eq!(Status::Ok.to_string(), "ok");
87 assert_eq!(Status::Dead.to_string(), "dead");
88 assert_eq!(Status::Redirect.to_string(), "redirect");
89 assert_eq!(Status::Paywall.to_string(), "paywall");
90 assert_eq!(Status::Login.to_string(), "login");
91 }
92
93 #[test]
94 fn test_deserialize_status() {
95 let json = r#""ok""#;
96 let status: Status = serde_json::from_str(json).unwrap();
97 assert_eq!(status, Status::Ok);
98 }
99
100 #[test]
101 fn test_serialize_reference() {
102 let reference = Reference {
103 url: "https://example.com".to_string(),
104 title: "Example".to_string(),
105 categories: vec!["test".to_string()],
106 cited_in: vec!["README.md".to_string()],
107 status: Status::Pending,
108 verified: None,
109 notes: None,
110 };
111 let yaml = serde_yaml::to_string(&reference).unwrap();
112 assert!(yaml.contains("url: https://example.com"));
113 assert!(yaml.contains("status: pending"));
114 assert!(!yaml.contains("verified:"));
116 assert!(!yaml.contains("notes:"));
117 }
118
119 #[test]
120 fn test_full_file_roundtrip() {
121 let file = ReferencesFile {
122 meta: Meta {
123 created: "2025-12-15".to_string(),
124 last_verified: None,
125 tool: "ref".to_string(),
126 total_links: 1,
127 },
128 references: vec![Reference {
129 url: "https://example.com".to_string(),
130 title: "Example".to_string(),
131 categories: vec!["test".to_string()],
132 cited_in: vec!["README.md".to_string()],
133 status: Status::Ok,
134 verified: Some("2025-12-15T10:00:00Z".to_string()),
135 notes: None,
136 }],
137 };
138 let yaml = serde_yaml::to_string(&file).unwrap();
139 let parsed: ReferencesFile = serde_yaml::from_str(&yaml).unwrap();
140 assert_eq!(parsed.meta.total_links, 1);
141 assert_eq!(parsed.references[0].status, Status::Ok);
142 }
143}