Skip to main content

talk_core/
questions.rs

1use serde::Deserialize;
2
3#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
4pub struct Question {
5    /// Immutable identity — binds the thread, never derived from `text`.
6    pub id: String,
7    pub text: String,
8    /// Optional authored filename slug; if absent, the binary derives one from
9    /// `id`. Stored in the spec's TOML schema, so the struct must accept it.
10    #[serde(default)]
11    pub slug: Option<String>,
12    #[serde(default = "default_addressee")]
13    pub addressee: String,
14    #[serde(default = "default_cadence")]
15    pub cadence: String, // "daily" or "held:N"
16    #[serde(default)]
17    pub slot: Option<String>, // "morning" / "evening" / None
18}
19
20fn default_addressee() -> String { "self".into() }
21fn default_cadence() -> String { "daily".into() }
22
23#[derive(Clone, Debug, Deserialize)]
24pub struct Pack {
25    pub name: String,
26    #[serde(default)]
27    pub description: String,
28    #[serde(rename = "questions", default)]
29    pub questions: Vec<Question>,
30}
31
32impl Pack {
33    pub fn from_toml(s: &str) -> Result<Pack, toml::de::Error> {
34        toml::from_str(s)
35    }
36
37    /// Parse "held:N" → Some(N); anything else → None.
38    pub fn held_len(cadence: &str) -> Option<u32> {
39        cadence.strip_prefix("held:").and_then(|n| n.parse().ok())
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn loads_a_pack_with_defaults() {
49        let toml = r#"
50            name = "future-self"
51            description = "Talk to the you that's coming."
52            [[questions]]
53            id = "scared-of"
54            text = "Tell next-December you what you're scared of."
55            addressee = "future-self"
56            [[questions]]
57            id = "held-avoid"
58            text = "What am I avoiding?"
59            cadence = "held:7"
60        "#;
61        let pack = Pack::from_toml(toml).unwrap();
62        assert_eq!(pack.name, "future-self");
63        assert_eq!(pack.questions.len(), 2);
64        assert_eq!(pack.questions[0].addressee, "future-self");
65        assert_eq!(pack.questions[1].cadence, "held:7");
66        assert_eq!(pack.questions[0].cadence, "daily"); // default
67        assert_eq!(Pack::held_len("held:7"), Some(7));
68        assert_eq!(Pack::held_len("daily"), None);
69    }
70}