docbox_core/folders/
delete_folder.rs1use crate::events::{TenantEventMessage, TenantEventPublisher};
2use crate::files::delete_file::{DeleteFileError, delete_file};
3use crate::folders::folder_stream::FolderWalkStream;
4use crate::links::delete_link::{DeleteLinkError, delete_link};
5use docbox_database::{
6 DbPool,
7 models::{document_box::WithScope, file::File, folder::Folder, link::Link},
8};
9use docbox_search::{SearchError, TenantSearchIndex};
10use docbox_storage::StorageLayer;
11use futures::StreamExt;
12use thiserror::Error;
13
14use super::folder_stream::FolderWalkItem;
15
16pub enum RemoveStackItem {
18 Folder(Folder),
20 EmptyFolder(Folder),
23 File(File),
25 Link(Link),
27}
28
29#[derive(Debug, Error)]
30pub enum DeleteFolderError {
31 #[error("failed to resolve folder for deletion")]
32 ResolveFolder,
33 #[error(transparent)]
34 Folder(#[from] InternalDeleteFolderError),
35 #[error(transparent)]
36 File(#[from] DeleteFileError),
37 #[error(transparent)]
38 Link(#[from] DeleteLinkError),
39}
40
41#[derive(Debug, Error)]
42pub enum InternalDeleteFolderError {
43 #[error(transparent)]
44 Search(#[from] SearchError),
45
46 #[error("failed to delete folder metadata")]
47 Database,
48}
49
50pub async fn delete_folder(
51 db: &DbPool,
52 storage: &StorageLayer,
53 search: &TenantSearchIndex,
54 events: &TenantEventPublisher,
55 folder: Folder,
56) -> Result<(), DeleteFolderError> {
57 let document_box = folder.document_box.clone();
58
59 let mut stream = FolderWalkStream::new(db, folder);
60
61 while let Some(result) = stream.next().await {
62 let item = result.map_err(|error| {
63 tracing::error!(?error, "failed to resolve folder for deletion");
64 DeleteFolderError::ResolveFolder
65 })?;
66
67 match item {
68 FolderWalkItem::Folder(folder) => {
69 internal_delete_folder(db, search, events, folder).await?;
70 }
71 FolderWalkItem::File(file) => {
72 delete_file(db, storage, search, events, file, document_box.clone()).await?;
73 }
74 FolderWalkItem::Link(link) => {
75 delete_link(db, search, events, link, document_box.clone()).await?;
76 }
77 }
78 }
79
80 Ok(())
81}
82
83async fn internal_delete_folder(
86 db: &DbPool,
87 search: &TenantSearchIndex,
88 events: &TenantEventPublisher,
89 folder: Folder,
90) -> Result<(), InternalDeleteFolderError> {
91 search.delete_data(folder.id).await?;
93
94 let result = folder.delete(db).await.map_err(|error| {
95 tracing::error!(?error, "failed to delete folder");
96 InternalDeleteFolderError::Database
97 })?;
98
99 let document_box = folder.document_box.clone();
100
101 if result.rows_affected() < 1 {
103 return Ok(());
104 }
105
106 events.publish_event(TenantEventMessage::FolderDeleted(WithScope::new(
108 folder,
109 document_box,
110 )));
111
112 Ok(())
113}