1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! **The config lineages one workspace holds** (yog's `docs/REMOTE.md` §9.3) —
//! the policy branches a conversation is born on, each with its tip and the
//! files that tip carries.
//!
//! It is the listing a config read indexes into: a lineage names a branch and
//! its `files` are the paths a `config` gesture may address on it, so the two
//! pickers above the editor are this one answer read twice (DESIGN §4.30).
//!
//! # The oid is carried in both spellings, and neither is derived
//!
//! `oid` and `short_oid` arrive together and this seat abbreviates nothing: how
//! many characters are unambiguous is a property of the repository the engine
//! holds, so a seat that truncated the full oid itself would be guessing at a
//! collision domain it cannot see.
use serde_json::Value;
use super::fields;
/// The kind token this reading answers to.
pub(crate) const KIND: &str = "lineages";
/// One config lineage.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Lineage {
/// The branch's name — the address a config gesture on it carries.
pub name: String,
/// Its tip commit, whole.
pub oid: String,
/// The same commit as the engine abbreviates it.
pub short_oid: String,
/// When that commit was made, in seconds since the epoch. Signed, because
/// clock skew between two machines is a fact and not an error.
pub committed: i64,
/// Every path the tip holds — what a read on this lineage may address.
pub files: Vec<String>,
}
/// One row of the listing.
pub(crate) fn row(value: &Value) -> Result<Lineage, String> {
let obj = value.as_object().ok_or("lineage: not a JSON object")?;
Ok(Lineage {
name: fields::text(obj, "name")?,
oid: fields::text(obj, "oid")?,
short_oid: fields::text(obj, "short_oid")?,
committed: fields::secs(obj, "committed")?,
files: fields::list(obj, "files", path)?,
})
}
/// One path on a lineage's tip.
fn path(value: &Value) -> Result<String, String> {
value
.as_str()
.map(str::to_owned)
.ok_or_else(|| "lineage: a file is not a string".to_owned())
}
impl Lineage {
/// **What the row says in one clause** — the branch, its tip as the engine
/// abbreviates it, and how much it holds.
pub fn line(&self) -> String {
format!(
"{} @{} {} file(s)",
self.name,
self.short_oid,
self.files.len()
)
}
}
#[cfg(test)]
mod tests;