Skip to content

Commit 6d0ccda

Browse files
πŸ“ Add docstrings to fix/templates-created-at-schema
Docstrings generation was requested by @dmfilipenko. * #1600 (comment) The following files were modified: * `crates/db-user/src/lib.rs` * `crates/db-user/src/templates_ops.rs` * `crates/db-user/src/templates_types.rs`
1 parent 7fca2a2 commit 6d0ccda

File tree

3 files changed

+113
-6
lines changed

3 files changed

+113
-6
lines changed

β€Žcrates/db-user/src/lib.rsβ€Ž

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl std::ops::Deref for UserDatabase {
142142
}
143143

144144
// Append only. Do not reorder.
145-
const MIGRATIONS: [&str; 27] = [
145+
const MIGRATIONS: [&str; 28] = [
146146
include_str!("./calendars_migration.sql"),
147147
include_str!("./configs_migration.sql"),
148148
include_str!("./events_migration.sql"),
@@ -170,8 +170,25 @@ const MIGRATIONS: [&str; 27] = [
170170
include_str!("./templates_migration_1.sql"),
171171
include_str!("./chat_conversations_migration.sql"),
172172
include_str!("./chat_messages_v2_migration.sql"),
173+
include_str!("./templates_migration_2.sql"),
173174
];
174175

176+
/// Run the bundled SQL migrations against the given database.
177+
///
178+
/// Applies the module's embedded migrations in order to bring the database schema up to date.
179+
///
180+
/// # Returns
181+
///
182+
/// `Ok(())` on success, `Err(crate::Error)` if obtaining a connection or applying migrations fails.
183+
///
184+
/// # Examples
185+
///
186+
/// ```no_run
187+
/// # async fn run() -> Result<(), crate::Error> {
188+
/// let db = /* obtain a UserDatabase */;
189+
/// migrate(&db).await?;
190+
/// # Ok(()) }
191+
/// ```
175192
pub async fn migrate(db: &UserDatabase) -> Result<(), crate::Error> {
176193
let conn = db.conn()?;
177194
hypr_db_core::migrate(&conn, MIGRATIONS.to_vec()).await?;
@@ -198,4 +215,4 @@ mod tests {
198215
let user_id = uuid::Uuid::new_v4().to_string();
199216
init::seed(&db, user_id).await.unwrap();
200217
}
201-
}
218+
}

β€Žcrates/db-user/src/templates_ops.rsβ€Ž

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ impl UserDatabase {
1919
Ok(items)
2020
}
2121

22+
/// Inserts a new template or updates an existing one identified by its `id`, then returns the stored template.
23+
///
24+
/// If a row with the same `id` already exists, this operation updates its `title`, `description`, `sections`, `tags`, and `context_option` and returns the resulting row; otherwise it inserts a new row and returns it. Errors are propagated for database or row-conversion failures.
25+
///
26+
/// # Examples
27+
///
28+
/// ```no_run
29+
/// # use crate::{UserDatabase, Template};
30+
/// # async fn example(db: &UserDatabase, template: Template) -> Result<(), crate::Error> {
31+
/// let stored = db.upsert_template(template).await?;
32+
/// // `stored` now contains the inserted or updated Template as persisted in the database.
33+
/// # Ok(())
34+
/// # }
35+
/// ```
36+
///
37+
/// Returns the inserted or updated `Template`.
2238
pub async fn upsert_template(&self, template: Template) -> Result<Template, crate::Error> {
2339
let conn = self.conn()?;
2440

@@ -31,15 +47,17 @@ impl UserDatabase {
3147
description,
3248
sections,
3349
tags,
34-
context_option
50+
context_option,
51+
created_at
3552
) VALUES (
3653
:id,
3754
:user_id,
3855
:title,
3956
:description,
4057
:sections,
4158
:tags,
42-
:context_option
59+
:context_option,
60+
:created_at
4361
) ON CONFLICT(id) DO UPDATE SET
4462
title = :title,
4563
description = :description,
@@ -55,6 +73,7 @@ impl UserDatabase {
5573
":sections": serde_json::to_string(&template.sections).unwrap(),
5674
":tags": serde_json::to_string(&template.tags).unwrap(),
5775
":context_option": template.context_option.as_deref().unwrap_or(""),
76+
":created_at": template.created_at,
5877
},
5978
)
6079
.await?;
@@ -77,6 +96,50 @@ impl UserDatabase {
7796
mod tests {
7897
use crate::{tests::setup_db, Human, Template};
7998

99+
/// Integration test that verifies listing templates for a user is empty, upserting a template, and then retrieving the inserted template.
100+
///
101+
/// This test:
102+
/// - Creates a test database and a Human (user).
103+
/// - Asserts that listing templates for the user initially returns zero results.
104+
/// - Inserts a Template using `upsert_template`.
105+
/// - Asserts that listing templates for the user returns one result afterwards.
106+
///
107+
/// # Examples
108+
///
109+
/// ```
110+
/// # async fn run_test(db: &crate::UserDatabase) {
111+
/// let human = db
112+
/// .upsert_human(crate::Human {
113+
/// full_name: Some("test".to_string()),
114+
/// ..crate::Human::default()
115+
/// })
116+
/// .await
117+
/// .unwrap();
118+
///
119+
/// let templates = db.list_templates(&human.id).await.unwrap();
120+
/// assert_eq!(templates.len(), 0);
121+
///
122+
/// let _template = db
123+
/// .upsert_template(crate::Template {
124+
/// id: uuid::Uuid::new_v4().to_string(),
125+
/// user_id: human.id.clone(),
126+
/// title: "test".to_string(),
127+
/// description: "test".to_string(),
128+
/// sections: vec![],
129+
/// tags: vec![],
130+
/// context_option: Some(
131+
/// r#"{"type":"tags","selections":["Meeting","Project A"]}"#.to_string(),
132+
/// ),
133+
/// created_at: chrono::Utc::now()
134+
/// .to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
135+
/// })
136+
/// .await
137+
/// .unwrap();
138+
///
139+
/// let templates = db.list_templates(&human.id).await.unwrap();
140+
/// assert_eq!(templates.len(), 1);
141+
/// # }
142+
/// ```
80143
#[tokio::test]
81144
async fn test_templates() {
82145
let db = setup_db().await;
@@ -103,11 +166,13 @@ mod tests {
103166
context_option: Some(
104167
r#"{"type":"tags","selections":["Meeting","Project A"]}"#.to_string(),
105168
),
169+
created_at: chrono::Utc::now()
170+
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
106171
})
107172
.await
108173
.unwrap();
109174

110175
let templates = db.list_templates(&human.id).await.unwrap();
111176
assert_eq!(templates.len(), 1);
112177
}
113-
}
178+
}

β€Žcrates/db-user/src/templates_types.rsβ€Ž

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ user_common_derives! {
99
pub sections: Vec<TemplateSection>,
1010
pub tags: Vec<String>,
1111
pub context_option: Option<String>,
12+
pub created_at: String,
1213
}
1314
}
1415

@@ -20,6 +21,27 @@ user_common_derives! {
2021
}
2122

2223
impl Template {
24+
/// Constructs a `Template` from a database row.
25+
///
26+
/// Maps columns to `Template` fields as follows:
27+
/// - column 0 β†’ `id` (panics if missing),
28+
/// - column 1 β†’ `user_id` (panics if missing),
29+
/// - column 2 β†’ `title` (panics if missing),
30+
/// - column 3 β†’ `description` (panics if missing),
31+
/// - column 4 β†’ `sections` (expects JSON string; defaults to empty `Vec` if absent),
32+
/// - column 5 β†’ `tags` (expects JSON string; defaults to empty `Vec` if absent),
33+
/// - column 6 β†’ `context_option` (set to `None` if missing or unreadable),
34+
/// - column 7 β†’ `created_at` (falls back to current UTC time in RFC3339 seconds precision if unavailable).
35+
///
36+
/// Note: JSON deserialization for `sections` and `tags` is unwrapped and will panic on invalid JSON. Required primitive columns (id, user_id, title, description) use `expect` and will panic if absent.
37+
///
38+
/// # Examples
39+
///
40+
/// ```ignore
41+
/// // `row` must be obtained from libsql query results.
42+
/// let template = Template::from_row(&row).expect("valid row");
43+
/// assert!(!template.id.is_empty());
44+
/// ```
2345
pub fn from_row(row: &libsql::Row) -> Result<Self, serde::de::value::Error> {
2446
Ok(Self {
2547
id: row.get(0).expect("id"),
@@ -35,6 +57,9 @@ impl Template {
3557
.map(|s| serde_json::from_str(s).unwrap())
3658
.unwrap_or_default(),
3759
context_option: row.get(6).ok(),
60+
created_at: row.get(7).unwrap_or_else(|_| {
61+
chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
62+
}),
3863
})
3964
}
40-
}
65+
}

0 commit comments

Comments
Β (0)