use std::fs::File; use std::path::PathBuf; use std::io::Read; use std::time::{SystemTime, UNIX_EPOCH}; pub struct Metadata { pub id: String, pub created_on: u128 } impl Metadata { pub fn serialize(&self) -> String { format!(r#"{{ "id": "{}", "created_on": {} }}"#, self.id, self.created_on) } pub fn deserialize(input: &str) -> Option { let clean_input = input .chars() .filter(|c| !c.is_whitespace()) .collect::(); let id = Metadata::read_field(&clean_input, "id")?; let created_on = Metadata::read_field(&clean_input, "created_on") // createdOn kept for compatibility with blog < 7.0.0 .or_else(|| Metadata::read_field(&clean_input, "createdOn"))?; Some(Metadata { id, created_on: created_on.parse().ok()? }) } pub fn read_or_create(file_path: &PathBuf) -> Metadata { match Metadata::read_metadata_file(file_path) { Some(metadata) => metadata, None => { let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .map(|duration| duration.as_millis() as u128) .unwrap_or_else(|_| 0); return Metadata { id: timestamp.to_string(), created_on: timestamp } } } } fn read_metadata_file(file_path: &PathBuf) -> Option { let mut file = File::open(file_path).ok()?; let mut contents = String::new(); file.read_to_string(&mut contents).ok()?; Metadata::deserialize(&contents) } fn read_field(input: &str, field_name: &str) -> Option { let key_pattern = format!(r#""{}":"#, field_name); input.find(&key_pattern) .and_then(|start| { let value_start = start + key_pattern.len(); let value_end = input[value_start..] .find(|c: char| c == ',' || c == '}' || c == '\n') .unwrap_or_else(|| input[value_start..].len()) + value_start; let raw_value = &input[value_start..value_end]; let value = raw_value.trim_matches('"').to_string(); Some(value) }) } }