1use crate::blob::{self, Blob};
12use crate::error::StorageError;
13use doido_core::Result;
14use doido_model::sea_orm::{
15 ConnectionTrait, DatabaseConnection, DbBackend, Statement, Value as DbValue,
16};
17
18fn db_err(e: impl std::fmt::Display) -> StorageError {
19 StorageError::Db(e.to_string())
20}
21
22pub async fn attach(
25 conn: &DatabaseConnection,
26 name: &str,
27 record_type: &str,
28 record_id: &str,
29 blob_key: &str,
30) -> Result<()> {
31 let stmt = Statement::from_sql_and_values(
32 DbBackend::Sqlite,
33 "INSERT INTO storage_attachments (name, record_type, record_id, blob_key, created_at) \
34 VALUES (?, ?, ?, ?, ?)",
35 [
36 DbValue::from(name),
37 DbValue::from(record_type),
38 DbValue::from(record_id),
39 DbValue::from(blob_key),
40 DbValue::from(chrono::Utc::now().to_rfc3339()),
41 ],
42 );
43 conn.execute_raw(stmt).await.map_err(db_err)?;
44 Ok(())
45}
46
47pub async fn replace_one(
50 conn: &DatabaseConnection,
51 name: &str,
52 record_type: &str,
53 record_id: &str,
54 blob_key: &str,
55) -> Result<()> {
56 detach(conn, name, record_type, record_id).await?;
57 attach(conn, name, record_type, record_id, blob_key).await
58}
59
60pub async fn attached_keys(
62 conn: &DatabaseConnection,
63 name: &str,
64 record_type: &str,
65 record_id: &str,
66) -> Result<Vec<String>> {
67 let stmt = Statement::from_sql_and_values(
68 DbBackend::Sqlite,
69 "SELECT blob_key FROM storage_attachments \
70 WHERE name = ? AND record_type = ? AND record_id = ? ORDER BY id ASC",
71 [
72 DbValue::from(name),
73 DbValue::from(record_type),
74 DbValue::from(record_id),
75 ],
76 );
77 let rows = conn.query_all_raw(stmt).await.map_err(db_err)?;
78 rows.into_iter()
79 .map(|r| {
80 r.try_get::<String>("", "blob_key")
81 .map_err(|e| db_err(e).into())
82 })
83 .collect()
84}
85
86pub async fn one(
88 conn: &DatabaseConnection,
89 name: &str,
90 record_type: &str,
91 record_id: &str,
92) -> Result<Option<Blob>> {
93 let keys = attached_keys(conn, name, record_type, record_id).await?;
94 match keys.first() {
95 Some(key) => blob::find(conn, key).await,
96 None => Ok(None),
97 }
98}
99
100pub async fn many(
102 conn: &DatabaseConnection,
103 name: &str,
104 record_type: &str,
105 record_id: &str,
106) -> Result<Vec<Blob>> {
107 let keys = attached_keys(conn, name, record_type, record_id).await?;
108 let mut blobs = Vec::with_capacity(keys.len());
109 for key in keys {
110 if let Some(b) = blob::find(conn, &key).await? {
111 blobs.push(b);
112 }
113 }
114 Ok(blobs)
115}
116
117pub async fn detach(
119 conn: &DatabaseConnection,
120 name: &str,
121 record_type: &str,
122 record_id: &str,
123) -> Result<()> {
124 let stmt = Statement::from_sql_and_values(
125 DbBackend::Sqlite,
126 "DELETE FROM storage_attachments WHERE name = ? AND record_type = ? AND record_id = ?",
127 [
128 DbValue::from(name),
129 DbValue::from(record_type),
130 DbValue::from(record_id),
131 ],
132 );
133 conn.execute_raw(stmt).await.map_err(db_err)?;
134 Ok(())
135}
136
137pub async fn all_keys_for_record(
140 conn: &DatabaseConnection,
141 record_type: &str,
142 record_id: &str,
143) -> Result<Vec<String>> {
144 let stmt = Statement::from_sql_and_values(
145 DbBackend::Sqlite,
146 "SELECT blob_key FROM storage_attachments WHERE record_type = ? AND record_id = ?",
147 [DbValue::from(record_type), DbValue::from(record_id)],
148 );
149 let rows = conn.query_all_raw(stmt).await.map_err(db_err)?;
150 rows.into_iter()
151 .map(|r| {
152 r.try_get::<String>("", "blob_key")
153 .map_err(|e| db_err(e).into())
154 })
155 .collect()
156}
157
158pub async fn delete_for_blob(conn: &DatabaseConnection, blob_key: &str) -> Result<()> {
160 let stmt = Statement::from_sql_and_values(
161 DbBackend::Sqlite,
162 "DELETE FROM storage_attachments WHERE blob_key = ?",
163 [DbValue::from(blob_key)],
164 );
165 conn.execute_raw(stmt).await.map_err(db_err)?;
166 Ok(())
167}
168
169pub async fn detach_blob(
171 conn: &DatabaseConnection,
172 name: &str,
173 record_type: &str,
174 record_id: &str,
175 blob_key: &str,
176) -> Result<()> {
177 let stmt = Statement::from_sql_and_values(
178 DbBackend::Sqlite,
179 "DELETE FROM storage_attachments \
180 WHERE name = ? AND record_type = ? AND record_id = ? AND blob_key = ?",
181 [
182 DbValue::from(name),
183 DbValue::from(record_type),
184 DbValue::from(record_id),
185 DbValue::from(blob_key),
186 ],
187 );
188 conn.execute_raw(stmt).await.map_err(db_err)?;
189 Ok(())
190}