coven_database/
file_location.rs1use crate::DbError;
5use coven_crypto::{FileKey, SecretText};
6use coven_format::{
7 coordinates::Coordinates, file_reference::FileReference, path::ObjectPath, value::Value,
8};
9use coven_foundation::id_source::{DeviceId, IdSource, ObjectId, StoreId};
10
11#[derive(Clone, Debug)]
15pub struct FileLocation {
16 uploader: DeviceId,
17 object: ObjectId,
18 text: SecretText,
19}
20
21impl FileLocation {
22 pub fn uploader(&self) -> DeviceId {
24 self.uploader
25 }
26
27 pub fn object(&self) -> ObjectId {
30 self.object
31 }
32
33 pub fn path(&self, store: StoreId) -> ObjectPath {
35 ObjectPath::file(store, self.uploader, self.object)
36 }
37
38 pub(crate) fn new(device: DeviceId, ids: &dyn IdSource) -> Result<Self, DbError> {
41 Self::from_reference(&FileReference {
42 device,
43 object: ObjectId(ids.new_id()),
44 key: FileKey::generate().map_err(DbError::FileKey)?,
45 })
46 }
47
48 fn from_reference(reference: &FileReference) -> Result<Self, DbError> {
49 Ok(Self { uploader: reference.device, object: reference.object, text: reference.encode() })
50 }
51
52 #[expect(
53 clippy::map_err_ignore,
54 reason = "a stored value that won't decode is DamagedDatabase"
55 )]
56 pub(crate) fn decode(text: &str) -> Result<Self, DbError> {
57 let reference = FileReference::decode(text).map_err(|_| DbError::DamagedDatabase)?;
58 Self::from_reference(&reference)
59 }
60
61 pub(crate) fn text(&self) -> &str {
62 self.text.as_str()
63 }
64}
65
66impl PartialEq for FileLocation {
67 fn eq(&self, other: &Self) -> bool {
68 self.text.as_str() == other.text.as_str()
69 }
70}
71
72pub trait FileLocationKey {
75 fn reference(&self) -> Result<FileReference, DbError>;
77}
78
79impl FileLocationKey for FileLocation {
80 fn reference(&self) -> Result<FileReference, DbError> {
81 Ok(FileReference::decode(self.text.as_str())?)
82 }
83}
84
85#[derive(Clone, Debug, PartialEq)]
87pub(crate) enum Place {
88 Cloud(FileLocation),
90 DeviceOnly(DeviceId),
92}
93
94impl Place {
95 #[expect(
97 clippy::map_err_ignore,
98 reason = "a stored value that won't decode is DamagedDatabase"
99 )]
100 pub(crate) fn decode(value: &Value) -> Result<Self, DbError> {
101 let Value::Text(text) = value else {
102 return Err(DbError::DamagedDatabase);
103 };
104 match Coordinates::decode(text).map_err(|_| DbError::DamagedDatabase)? {
105 Coordinates::Cloud(reference) => {
106 Ok(Self::Cloud(FileLocation::from_reference(&reference)?))
107 }
108 Coordinates::DeviceOnly(device) => Ok(Self::DeviceOnly(device)),
109 }
110 }
111
112 pub(crate) fn value(&self) -> Value {
114 Value::Text(match self {
115 Self::Cloud(location) => location.text().to_owned(),
116 Self::DeviceOnly(device) => {
117 Coordinates::DeviceOnly(*device).encode().as_str().to_owned()
118 }
119 })
120 }
121}
122
123#[cfg(test)]
124#[path = "file_location_tests.rs"]
125mod tests;