1extern crate alloc;
4use alloc::collections::BTreeMap;
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::fmt;
8
9use crate::entity::PropertyValue;
10use crate::{Header, Timestamp};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
16pub enum NoteStatus {
17 #[default]
18 Active,
19 Archived,
20 Deleted,
21}
22
23impl NoteStatus {
24 pub const fn name(self) -> &'static str {
25 match self {
26 Self::Active => "active",
27 Self::Archived => "archived",
28 Self::Deleted => "deleted",
29 }
30 }
31}
32
33impl fmt::Display for NoteStatus {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 f.write_str(self.name())
36 }
37}
38
39impl core::str::FromStr for NoteStatus {
40 type Err = crate::error::UnknownVariant;
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 match s.trim().to_ascii_lowercase().as_str() {
43 "active" => Ok(Self::Active),
44 "archived" => Ok(Self::Archived),
45 "deleted" => Ok(Self::Deleted),
46 other => Err(crate::error::UnknownVariant::new(
47 "note_status",
48 other,
49 &["active", "archived", "deleted"],
50 )),
51 }
52 }
53}
54
55#[derive(Clone, Debug)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58pub struct Note {
59 #[cfg_attr(feature = "serde", serde(flatten))]
60 pub header: Header,
61 pub kind: String,
62 pub status: NoteStatus,
63 pub content: String,
64 pub properties: BTreeMap<String, PropertyValue>,
65 pub tags: Vec<String>,
66 pub salience: Option<f64>,
67 pub decay_factor: Option<f64>,
68 pub expires_at: Option<Timestamp>,
69 pub deleted_at: Option<Timestamp>,
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::{Id128, Namespace};
76
77 fn test_header() -> Header {
78 Header::new(
79 Id128::from_u128(1),
80 Namespace::local(),
81 Timestamp::from_secs(1700000000),
82 )
83 }
84
85 #[test]
86 fn note_construction() {
87 let note = Note {
88 header: test_header(),
89 kind: String::from("decision"),
90 status: NoteStatus::Active,
91 content: String::from("Use BGE-base for multilingual corpus"),
92 properties: BTreeMap::new(),
93 tags: alloc::vec!["retrieval".into()],
94 salience: Some(0.8),
95 decay_factor: Some(0.01),
96 expires_at: None,
97 deleted_at: None,
98 };
99 assert_eq!(note.kind, "decision");
100 assert_eq!(note.tags.len(), 1);
101 }
102
103 #[test]
104 fn note_construction_uses_pack_owned_kind_string() {
105 let note = Note {
106 header: test_header(),
107 kind: String::from("decision"),
108 status: NoteStatus::Active,
109 content: String::from("test"),
110 properties: BTreeMap::new(),
111 tags: alloc::vec![],
112 salience: None,
113 decay_factor: None,
114 expires_at: None,
115 deleted_at: None,
116 };
117 assert_eq!(note.kind, "decision");
118 }
119
120 #[test]
121 fn note_status_deleted_roundtrip() {
122 use core::str::FromStr;
123 assert_eq!(
124 NoteStatus::from_str("deleted").unwrap(),
125 NoteStatus::Deleted
126 );
127 assert_eq!(NoteStatus::Deleted.name(), "deleted");
128 }
129}