coven_database/
file_database_pins.rs1use super::{file_ref, FileDatabase};
5use crate::{DbError, FileRef};
6use coven_foundation::files::StagedName;
7
8impl FileDatabase {
9 pub async fn pin_requests(&self) -> Result<Vec<crate::PinRequest>, DbError> {
12 self.read(crate::file_pins::read).await
13 }
14 pub async fn end_pin_request(&self, id: i64) -> Result<(), DbError> {
18 self.database
19 .call(move |inner| {
20 inner.with_files("end a pin request", |writer, _| {
22 writer.state_transaction(|db| crate::file_pins::end_request(db, id).map(drop))
23 })
24 })
25 .await
26 }
27 pub async fn keep(&self, prepared: crate::PreparedPin, id: i64) -> Result<bool, DbError> {
32 self.database
33 .write(move |sql| {
34 if !sql.end_pin_request(id)? {
35 return Ok(false);
36 }
37 sql.pin(prepared)?;
38 Ok(true)
39 })
40 .await
41 }
42 pub async fn drop_pin_copy(&self, id: i64) -> Result<(), DbError> {
45 self.database
46 .call(move |inner| {
47 inner.with_files("drop a pin's copy", |writer, _| {
48 writer.transaction(|db| crate::file_pins::drop_copy(db, id))
49 })
50 })
51 .await
52 }
53 pub async fn fail_pin<P: crate::PendingValue>(
57 &self,
58 request: &crate::PinRequest,
59 record: P,
60 retry: Option<crate::RetryWait>,
61 ) -> Result<(), DbError> {
62 let (id, file) = (request.id, request.file);
63 self.run(move |db, _, _, _| {
64 db.state_transaction(|db| {
65 if record.pin_of() != Some(file) {
66 return Err(DbError::Invariant {
67 what: "a kept pin's failure is its own record",
68 });
69 }
70 if !db.query_row(
71 "SELECT EXISTS(SELECT 1 FROM _coven_pin_requests WHERE id=?1)",
72 [id],
73 |r| r.get::<_, bool>(0),
74 )? {
75 return Ok(());
76 }
77 crate::retry::save_failure(
78 db,
79 &record,
80 crate::RetrySubject::Pin(id),
81 retry.as_ref(),
82 )
83 })
84 })
85 .await
86 }
87 #[expect(
90 clippy::map_err_ignore,
91 reason = "a stored value that won't convert is damage, a computed one a broken invariant"
92 )]
93 pub async fn copy_chunk_hash(
94 &self,
95 copy: &StagedName,
96 index: u64,
97 ) -> Result<coven_crypto::ContentHash, DbError> {
98 let copy = copy.as_str().to_owned();
99 self.read(move |db| {
100 let index = i64::try_from(index)
101 .map_err(|_| DbError::Invariant { what: "a chunk index fits SQLite" })?;
102 let hash = db
103 .query(
104 "SELECT hash FROM _coven_copy_chunks WHERE path=?1 AND chunk=?2",
105 rusqlite::params![copy, index],
106 |r| r.get::<_, Vec<u8>>(0),
107 )?
108 .pop()
109 .ok_or(DbError::DamagedDatabase)?;
110 crate::file_hashes::stored_hash(hash)
111 })
112 .await
113 }
114 pub async fn open_pin_copy(
117 &self,
118 file: &FileRef,
119 copy: &StagedName,
120 ) -> Result<crate::LocalFileStream, crate::LocalFileError> {
121 let (id, size, copy) = (file.id().to_string(), file.size(), copy.clone());
122 self.database
123 .call(move |inner| {
124 file_ref::open_source(
125 &inner.access.directory,
126 &id,
127 size,
128 file_ref::LocalSource::Copy(copy),
129 )
130 })
131 .await
132 }
133}