//! Versions of main. Each landing adds one; each version names the one //! before it, so main is a hash chain. use serde::{Deserialize, Serialize}; use crate::Id; use crate::id::sha256; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Version { pub n: u64, pub tree: Id, /// The previous version's ID (None for version 1). pub prev: Option, /// The change that landed (empty for imported history). pub change: String, /// "Name ", the person (or agent acting for one) who wrote it. pub author: String, /// Seconds since the epoch. pub time: i64, pub title: String, #[serde(default)] pub body: String, /// The git commit this version is in the project's git view, once known. #[serde(default)] pub git: Option, } impl Version { /// The version's ID: a hash of everything but its git mapping, which is /// filled in later. pub fn id(&self, project: &str) -> Id { let text = format!( "vcs version\nproject {project}\nn {}\ntree {}\nprev {}\nchange {}\nauthor {}\ntime {}\n\n{}\n\n{}", self.n, self.tree, self.prev.map(|p| p.hex()).unwrap_or_else(|| "-".into()), self.change, self.author, self.time, self.title, self.body ); sha256(&[text.as_bytes()]) } /// The description as one text: title, a blank line, then the body. pub fn message(&self) -> String { if self.body.is_empty() { self.title.clone() } else { format!("{}\n\n{}", self.title, self.body) } } } #[cfg(test)] mod tests { use super::*; #[test] fn ids_chain() { let v1 = Version { n: 1, tree: Id::ZERO, prev: None, change: "bcdf".into(), author: "a ".into(), time: 1, title: "t".into(), body: String::new(), git: None }; let v2 = Version { n: 2, prev: Some(v1.id("p")), ..v1.clone() }; assert_ne!(v1.id("p"), v2.id("p")); let mut mapped = v1.clone(); mapped.git = Some("abc".into()); assert_eq!(mapped.id("p"), v1.id("p")); } }