Skip to main content

systemprompt_files/models/
metadata.rs

1//! Structured metadata stored alongside file rows.
2//!
3//! [`FileMetadata`] is the top-level container, carrying optional
4//! [`FileChecksums`] and a [`TypeSpecificMetadata`] variant
5//! ([`DocumentMetadata`], [`AudioMetadata`], or [`VideoMetadata`]; image detail
6//! lives in the sibling `image_metadata` module). Each type offers a builder
7//! API for incremental construction.
8
9use serde::{Deserialize, Serialize};
10
11pub(super) use super::image_metadata::ImageMetadata;
12
13#[derive(Debug, Clone, Default, Serialize, Deserialize)]
14pub struct FileMetadata {
15    #[serde(default, skip_serializing_if = "Option::is_none")]
16    pub checksums: Option<FileChecksums>,
17
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub type_specific: Option<TypeSpecificMetadata>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23#[serde(tag = "type", rename_all = "snake_case")]
24pub enum TypeSpecificMetadata {
25    Image(ImageMetadata),
26    Document(DocumentMetadata),
27    Audio(AudioMetadata),
28    Video(VideoMetadata),
29}
30
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
32pub struct DocumentMetadata {
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub title: Option<String>,
35
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub author: Option<String>,
38
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub page_count: Option<u32>,
41}
42
43#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
44pub struct AudioMetadata {
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub duration_seconds: Option<f32>,
47
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub sample_rate: Option<u32>,
50
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub channels: Option<u8>,
53}
54
55#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
56pub struct VideoMetadata {
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub width: Option<u32>,
59
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub height: Option<u32>,
62
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub duration_seconds: Option<f32>,
65
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub frame_rate: Option<f32>,
68}
69
70#[derive(Debug, Clone, Default, Serialize, Deserialize)]
71pub struct FileChecksums {
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub md5: Option<String>,
74
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub sha256: Option<String>,
77}
78
79impl FileMetadata {
80    pub const fn new() -> Self {
81        Self {
82            checksums: None,
83            type_specific: None,
84        }
85    }
86
87    pub fn with_image(mut self, image: ImageMetadata) -> Self {
88        self.type_specific = Some(TypeSpecificMetadata::Image(image));
89        self
90    }
91
92    pub fn with_document(mut self, doc: DocumentMetadata) -> Self {
93        self.type_specific = Some(TypeSpecificMetadata::Document(doc));
94        self
95    }
96
97    pub fn with_audio(mut self, audio: AudioMetadata) -> Self {
98        self.type_specific = Some(TypeSpecificMetadata::Audio(audio));
99        self
100    }
101
102    pub fn with_video(mut self, video: VideoMetadata) -> Self {
103        self.type_specific = Some(TypeSpecificMetadata::Video(video));
104        self
105    }
106
107    pub fn with_checksums(mut self, checksums: FileChecksums) -> Self {
108        self.checksums = Some(checksums);
109        self
110    }
111}
112
113impl DocumentMetadata {
114    pub const fn new() -> Self {
115        Self {
116            title: None,
117            author: None,
118            page_count: None,
119        }
120    }
121
122    pub fn with_title(mut self, title: impl Into<String>) -> Self {
123        self.title = Some(title.into());
124        self
125    }
126
127    pub fn with_author(mut self, author: impl Into<String>) -> Self {
128        self.author = Some(author.into());
129        self
130    }
131
132    pub const fn with_page_count(mut self, page_count: u32) -> Self {
133        self.page_count = Some(page_count);
134        self
135    }
136}
137
138impl AudioMetadata {
139    pub const fn new() -> Self {
140        Self {
141            duration_seconds: None,
142            sample_rate: None,
143            channels: None,
144        }
145    }
146
147    pub const fn with_duration_seconds(mut self, duration: f32) -> Self {
148        self.duration_seconds = Some(duration);
149        self
150    }
151
152    pub const fn with_sample_rate(mut self, sample_rate: u32) -> Self {
153        self.sample_rate = Some(sample_rate);
154        self
155    }
156
157    pub const fn with_channels(mut self, channels: u8) -> Self {
158        self.channels = Some(channels);
159        self
160    }
161}
162
163impl VideoMetadata {
164    pub const fn new() -> Self {
165        Self {
166            width: None,
167            height: None,
168            duration_seconds: None,
169            frame_rate: None,
170        }
171    }
172
173    pub const fn with_dimensions(mut self, width: u32, height: u32) -> Self {
174        self.width = Some(width);
175        self.height = Some(height);
176        self
177    }
178
179    pub const fn with_duration_seconds(mut self, duration: f32) -> Self {
180        self.duration_seconds = Some(duration);
181        self
182    }
183
184    pub const fn with_frame_rate(mut self, frame_rate: f32) -> Self {
185        self.frame_rate = Some(frame_rate);
186        self
187    }
188}
189
190impl FileChecksums {
191    pub const fn new() -> Self {
192        Self {
193            md5: None,
194            sha256: None,
195        }
196    }
197
198    pub fn with_md5(mut self, md5: impl Into<String>) -> Self {
199        self.md5 = Some(md5.into());
200        self
201    }
202
203    pub fn with_sha256(mut self, sha256: impl Into<String>) -> Self {
204        self.sha256 = Some(sha256.into());
205        self
206    }
207}