docbox_database/models/
generated_file.rs1use std::str::FromStr;
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use sqlx::{postgres::PgQueryResult, prelude::FromRow};
6use utoipa::ToSchema;
7use uuid::Uuid;
8
9use super::{document_box::DocumentBoxScopeRaw, file::FileId};
10use crate::{DbExecutor, DbResult};
11
12pub type GeneratedFileId = Uuid;
13
14#[derive(
15 Debug,
16 Clone,
17 Copy,
18 strum::EnumString,
19 strum::Display,
20 Deserialize,
21 Serialize,
22 ToSchema,
23 PartialEq,
24 Eq,
25)]
26pub enum GeneratedFileType {
27 Pdf,
29 CoverPage,
31 SmallThumbnail,
33 LargeThumbnail,
35 TextContent,
37 HtmlContent,
39 Metadata,
42}
43
44impl TryFrom<String> for GeneratedFileType {
45 type Error = strum::ParseError;
46 fn try_from(value: String) -> Result<Self, Self::Error> {
47 GeneratedFileType::from_str(&value)
48 }
49}
50
51#[derive(Debug, Clone, FromRow, Serialize, ToSchema)]
53pub struct GeneratedFile {
54 #[schema(value_type = Uuid)]
56 pub id: GeneratedFileId,
57 #[schema(value_type = Uuid)]
59 pub file_id: FileId,
60 pub mime: String,
62 #[sqlx(rename = "type")]
64 #[serde(rename = "type")]
65 #[sqlx(try_from = "String")]
66 pub ty: GeneratedFileType,
67 pub hash: String,
69 #[serde(skip)]
71 pub file_key: String,
72 pub created_at: DateTime<Utc>,
74}
75
76impl Eq for GeneratedFile {}
77
78impl PartialEq for GeneratedFile {
79 fn eq(&self, other: &Self) -> bool {
80 self.id.eq(&other.id)
81 && self.file_id.eq(&other.file_id)
82 && self.mime.eq(&other.mime)
83 && self.ty.eq(&other.ty)
84 && self.hash.eq(&other.hash)
85 && self.file_key.eq(&other.file_key)
86 && self
89 .created_at
90 .timestamp_millis()
91 .eq(&other.created_at.timestamp_millis())
92 }
93}
94
95#[derive(Debug)]
96pub struct CreateGeneratedFile {
97 pub id: Uuid,
98 pub file_id: FileId,
99 pub mime: String,
100 pub ty: GeneratedFileType,
101 pub hash: String,
102 pub file_key: String,
103 pub created_at: DateTime<Utc>,
104}
105
106impl GeneratedFile {
107 pub async fn create(
108 db: impl DbExecutor<'_>,
109 CreateGeneratedFile {
110 id,
111 file_id,
112 ty,
113 hash,
114 file_key,
115 mime,
116 created_at,
117 }: CreateGeneratedFile,
118 ) -> DbResult<GeneratedFile> {
119 sqlx::query(
120 r#"
121 INSERT INTO "docbox_generated_files"
122 ("id", "file_id", "mime", "type", "hash", "file_key", "created_at")
123 VALUES ($1, $2, $3, $4, $5, $6, $7)
124 "#,
125 )
126 .bind(id)
127 .bind(file_id)
128 .bind(mime.as_str())
129 .bind(ty.to_string())
130 .bind(hash.as_str())
131 .bind(file_key.as_str())
132 .bind(created_at)
133 .execute(db)
134 .await?;
135
136 Ok(GeneratedFile {
137 id,
138 file_id,
139 mime,
140 ty,
141 hash,
142 file_key,
143 created_at,
144 })
145 }
146
147 pub async fn delete(self, db: impl DbExecutor<'_>) -> DbResult<PgQueryResult> {
149 sqlx::query(r#"DELETE FROM "docbox_generated_files" WHERE "id" = $1"#)
150 .bind(self.id)
151 .execute(db)
152 .await
153 }
154
155 pub async fn find_all(
156 db: impl DbExecutor<'_>,
157 file_id: FileId,
158 ) -> DbResult<Vec<GeneratedFile>> {
159 sqlx::query_as(r#"SELECT * FROM "docbox_generated_files" WHERE "file_id" = $1"#)
160 .bind(file_id)
161 .fetch_all(db)
162 .await
163 }
164
165 pub async fn find(
167 db: impl DbExecutor<'_>,
168 scope: &DocumentBoxScopeRaw,
169 file_id: FileId,
170 ty: GeneratedFileType,
171 ) -> DbResult<Option<GeneratedFile>> {
172 sqlx::query_as(
173 r#"
174 SELECT "gen".*
175 FROM "docbox_generated_files" "gen"
176 -- Join on the file itself
177 INNER JOIN "docbox_files" "file" ON "gen".file_id = "file"."id"
178 -- Join to the file parent folder
179 INNER JOIN "docbox_folders" "folder" ON "file"."folder_id" = "folder"."id"
180 -- Only find the matching type for the specified file
181 WHERE "file"."id" = $1 AND "folder"."document_box" = $2 AND "gen"."type" = $3
182 "#,
183 )
184 .bind(file_id)
185 .bind(scope)
186 .bind(ty.to_string())
187 .fetch_optional(db)
188 .await
189 }
190}