1use crate::{CreateNote, Database, Error, Note, NoteQuery, NoteSummary, TagCount, UpdateNote};
2
3pub struct VetaService<D: Database> {
6 db: D,
7}
8
9impl<D: Database> VetaService<D> {
10 pub fn new(db: D) -> Self {
11 Self { db }
12 }
13
14 pub async fn add_note(
16 &self,
17 title: String,
18 body: String,
19 tags: Vec<String>,
20 references: Vec<String>,
21 ) -> Result<i64, Error> {
22 let title = title.trim().to_string();
24 if title.is_empty() {
25 return Err(Error::Validation("title cannot be empty".into()));
26 }
27
28 let mut tags: Vec<String> = tags
30 .into_iter()
31 .map(|t| t.trim().to_lowercase())
32 .filter(|t| !t.is_empty())
33 .collect();
34 tags.sort();
35 tags.dedup();
36
37 let mut references: Vec<String> = references
39 .into_iter()
40 .map(|r| r.trim().to_string())
41 .filter(|r| !r.is_empty())
42 .collect();
43 references.dedup();
44
45 self.db
46 .add_note(CreateNote {
47 title,
48 body,
49 tags,
50 references,
51 })
52 .await
53 }
54
55 pub async fn get_note(&self, id: i64) -> Result<Option<Note>, Error> {
57 self.db.get_note(id).await
58 }
59
60 pub async fn list_notes(&self, query: NoteQuery) -> Result<Vec<NoteSummary>, Error> {
62 let query = NoteQuery {
64 limit: match query.limit {
65 Some(0) => None,
66 Some(n) => Some(n),
67 None => Some(100),
68 },
69 ..query
70 };
71 let notes = self.db.list_notes(query).await?;
72 Ok(notes.into_iter().map(|n| n.to_summary(60)).collect())
73 }
74
75 pub async fn count_notes(&self, query: NoteQuery) -> Result<i64, Error> {
77 self.db.count_notes(query).await
78 }
79
80 pub async fn update_note(&self, id: i64, update: UpdateNote) -> Result<bool, Error> {
82 if let Some(ref title) = update.title {
84 if title.trim().is_empty() {
85 return Err(Error::Validation("title cannot be empty".into()));
86 }
87 }
88
89 let update = UpdateNote {
91 title: update.title.map(|t| t.trim().to_string()),
92 body: update.body,
93 tags: update.tags.map(|tags| {
94 let mut tags: Vec<String> = tags
95 .into_iter()
96 .map(|t| t.trim().to_lowercase())
97 .filter(|t| !t.is_empty())
98 .collect();
99 tags.sort();
100 tags.dedup();
101 tags
102 }),
103 references: update.references.map(|refs| {
104 let mut refs: Vec<String> = refs
105 .into_iter()
106 .map(|r| r.trim().to_string())
107 .filter(|r| !r.is_empty())
108 .collect();
109 refs.dedup();
110 refs
111 }),
112 };
113
114 self.db.update_note(id, update).await
115 }
116
117 pub async fn delete_note(&self, id: i64) -> Result<bool, Error> {
119 self.db.delete_note(id).await
120 }
121
122 pub async fn list_tags(&self) -> Result<Vec<TagCount>, Error> {
124 self.db.list_tags().await
125 }
126
127 pub async fn grep(
129 &self,
130 pattern: &str,
131 tags: Option<Vec<String>>,
132 case_sensitive: bool,
133 ) -> Result<Vec<NoteSummary>, Error> {
134 let notes = self
135 .db
136 .grep(pattern, tags.as_deref(), case_sensitive)
137 .await?;
138 Ok(notes.into_iter().map(|n| n.to_summary(60)).collect())
139 }
140}