docbox_core/folders/
folder_stream.rs1use docbox_database::{
2 DbErr, DbPool,
3 models::{
4 file::File,
5 folder::{Folder, ResolvedFolder},
6 link::Link,
7 },
8};
9use futures::{FutureExt, Stream, future::BoxFuture};
10use std::{collections::VecDeque, task::Poll};
11use thiserror::Error;
12
13#[derive(Debug)]
15pub enum FolderWalkItem {
16 Folder(Folder),
17 File(File),
18 Link(Link),
19}
20
21enum FolderWalkStackItem<'db> {
23 Unresolved(Folder),
25 Resolving {
27 folder: Folder,
29 future: BoxFuture<'db, Result<ResolvedFolder, DbErr>>,
31 },
32 Resolved(FolderWalkItem),
34}
35
36#[derive(Debug, Error)]
38pub enum FolderWalkError {
39 #[error("failed to resolve folder")]
40 ResolveFolder(DbErr),
41}
42
43pub struct FolderWalkStream<'db> {
51 db: &'db DbPool,
53
54 stack: VecDeque<FolderWalkStackItem<'db>>,
56}
57
58impl<'db> FolderWalkStream<'db> {
59 pub fn new(db: &'db DbPool, folder: Folder) -> FolderWalkStream<'db> {
60 let mut stack = VecDeque::new();
61 stack.push_back(FolderWalkStackItem::Unresolved(folder));
62
63 FolderWalkStream { db, stack }
64 }
65}
66
67impl<'db> Stream for FolderWalkStream<'db> {
68 type Item = Result<FolderWalkItem, FolderWalkError>;
69
70 fn poll_next(
71 self: std::pin::Pin<&mut Self>,
72 cx: &mut std::task::Context<'_>,
73 ) -> Poll<Option<Self::Item>> {
74 let this = self.get_mut();
75
76 while let Some(item) = this.stack.pop_front() {
77 match item {
78 FolderWalkStackItem::Resolving { folder, mut future } => {
80 let resolved = match future.poll_unpin(cx) {
81 Poll::Ready(Ok(value)) => value,
83
84 Poll::Ready(Err(error)) => {
86 return Poll::Ready(Some(Err(FolderWalkError::ResolveFolder(error))));
87 }
88
89 Poll::Pending => {
91 this.stack
92 .push_front(FolderWalkStackItem::Resolving { folder, future });
93 return Poll::Pending;
94 }
95 };
96
97 this.stack
99 .push_front(FolderWalkStackItem::Resolved(FolderWalkItem::Folder(
100 folder,
101 )));
102
103 for item in resolved.folders {
105 this.stack.push_front(FolderWalkStackItem::Unresolved(item));
106 }
107
108 for item in resolved.files {
110 this.stack
111 .push_front(FolderWalkStackItem::Resolved(FolderWalkItem::File(item)));
112 }
113
114 for item in resolved.links {
115 this.stack
116 .push_front(FolderWalkStackItem::Resolved(FolderWalkItem::Link(item)));
117 }
118 }
119
120 FolderWalkStackItem::Unresolved(folder) => {
122 let future = Box::pin(ResolvedFolder::resolve(this.db, folder.id));
123 this.stack
124 .push_front(FolderWalkStackItem::Resolving { folder, future });
125 }
126
127 FolderWalkStackItem::Resolved(item) => {
129 return Poll::Ready(Some(Ok(item)));
130 }
131 }
132 }
133
134 Poll::Ready(None)
136 }
137}