ferroui_base/platform/storage/
fallback_storage_provider.rs1use super::{
2 FilePickerOpenOptions, FilePickerSaveOptions, FolderPickerOpenOptions, IStorageBookmarkFile,
3 IStorageBookmarkFolder, IStorageFile, IStorageFolder, IStorageProvider, OpenFilePickerResult,
4 SaveFilePickerResult, WellKnownFolder,
5};
6use crate::input::LocalBoxFuture;
7use crate::utilities::Uri;
8use std::cell::{Cell, RefCell};
9use std::rc::Rc;
10
11pub type StorageProviderFactory = Rc<dyn Fn() -> LocalBoxFuture<Option<Rc<dyn IStorageProvider>>>>;
14
15struct State {
16 factories: RefCell<Rc<[StorageProviderFactory]>>,
17 providers: RefCell<Vec<Rc<dyn IStorageProvider>>>,
18 next_provider_factory: Cell<usize>,
19}
20
21impl State {
22 async fn get_provider(&self, index: usize) -> Option<Rc<dyn IStorageProvider>> {
26 loop {
27 if let Some(p) = self.providers.borrow().get(index) {
28 return Some(p.clone());
29 }
30
31 let factory = {
32 let factories = self.factories.borrow();
33 factories.get(self.next_provider_factory.get())?.clone()
34 };
35 let p = factory().await;
36 self.next_provider_factory.set(self.next_provider_factory.get() + 1);
37 if let Some(p) = p {
38 self.providers.borrow_mut().push(p);
39 }
40 }
41 }
42
43 async fn get_for(&self, filter: fn(&dyn IStorageProvider) -> bool) -> std::io::Result<Rc<dyn IStorageProvider>> {
44 let mut index = 0;
45 while let Some(p) = self.get_provider(index).await {
46 if filter(&*p) {
47 return Ok(p);
48 }
49 index += 1;
50 }
51 Err(std::io::Error::other("Unable to select a suitable storage provider"))
52 }
53
54 async fn first_not_null<TResult, F>(&self, cb: F) -> Option<TResult>
55 where
56 F: Fn(&dyn IStorageProvider) -> LocalBoxFuture<Option<TResult>>,
57 {
58 let mut index = 0;
59 while let Some(p) = self.get_provider(index).await {
60 let res = cb(&*p).await;
61 if res.is_some() {
62 return res;
63 }
64 index += 1;
65 }
66 None
67 }
68}
69
70pub struct FallbackStorageProvider {
75 state: Rc<State>,
76}
77
78impl FallbackStorageProvider {
79 pub fn new(factories: Vec<StorageProviderFactory>) -> Self {
82 Self {
83 state: Rc::new(State {
84 factories: RefCell::new(factories.into()),
85 providers: RefCell::new(Vec::new()),
86 next_provider_factory: Cell::new(0),
87 }),
88 }
89 }
90
91 pub fn reset(&self, factories: Vec<StorageProviderFactory>) {
93 *self.state.factories.borrow_mut() = factories.into();
94 self.state.providers.borrow_mut().clear();
95 self.state.next_provider_factory.set(0);
96 }
97}
98
99impl IStorageProvider for FallbackStorageProvider {
100 fn can_open(&self) -> bool {
103 true
104 }
105
106 fn can_save(&self) -> bool {
107 true
108 }
109
110 fn can_pick_folder(&self) -> bool {
111 true
112 }
113
114 fn open_file_picker_async(
115 &self,
116 options: FilePickerOpenOptions,
117 ) -> LocalBoxFuture<std::io::Result<Vec<Rc<dyn IStorageFile>>>> {
118 let state = self.state.clone();
119 Box::pin(async move { state.get_for(|p| p.can_open()).await?.open_file_picker_async(options).await })
120 }
121
122 fn open_file_picker_with_result_async(
123 &self,
124 options: FilePickerOpenOptions,
125 ) -> LocalBoxFuture<std::io::Result<OpenFilePickerResult>> {
126 let state = self.state.clone();
127 Box::pin(
128 async move { state.get_for(|p| p.can_open()).await?.open_file_picker_with_result_async(options).await },
129 )
130 }
131
132 fn save_file_picker_async(
133 &self,
134 options: FilePickerSaveOptions,
135 ) -> LocalBoxFuture<std::io::Result<Option<Rc<dyn IStorageFile>>>> {
136 let state = self.state.clone();
137 Box::pin(async move { state.get_for(|p| p.can_save()).await?.save_file_picker_async(options).await })
138 }
139
140 fn save_file_picker_with_result_async(
141 &self,
142 options: FilePickerSaveOptions,
143 ) -> LocalBoxFuture<std::io::Result<SaveFilePickerResult>> {
144 let state = self.state.clone();
145 Box::pin(
146 async move { state.get_for(|p| p.can_save()).await?.save_file_picker_with_result_async(options).await },
147 )
148 }
149
150 fn open_folder_picker_async(
151 &self,
152 options: FolderPickerOpenOptions,
153 ) -> LocalBoxFuture<std::io::Result<Vec<Rc<dyn IStorageFolder>>>> {
154 let state = self.state.clone();
155 Box::pin(async move { state.get_for(|p| p.can_pick_folder()).await?.open_folder_picker_async(options).await })
156 }
157
158 fn open_file_bookmark_async(&self, bookmark: &str) -> LocalBoxFuture<Option<Rc<dyn IStorageBookmarkFile>>> {
159 let state = self.state.clone();
160 let bookmark = bookmark.to_owned();
161 Box::pin(async move { state.first_not_null(|p| p.open_file_bookmark_async(&bookmark)).await })
162 }
163
164 fn open_folder_bookmark_async(&self, bookmark: &str) -> LocalBoxFuture<Option<Rc<dyn IStorageBookmarkFolder>>> {
165 let state = self.state.clone();
166 let bookmark = bookmark.to_owned();
167 Box::pin(async move { state.first_not_null(|p| p.open_folder_bookmark_async(&bookmark)).await })
168 }
169
170 fn try_get_file_from_path_async(&self, file_path: &Uri) -> LocalBoxFuture<Option<Rc<dyn IStorageFile>>> {
171 let state = self.state.clone();
172 let file_path = file_path.clone();
173 Box::pin(async move { state.first_not_null(|p| p.try_get_file_from_path_async(&file_path)).await })
174 }
175
176 fn try_get_folder_from_path_async(&self, folder_path: &Uri) -> LocalBoxFuture<Option<Rc<dyn IStorageFolder>>> {
177 let state = self.state.clone();
178 let folder_path = folder_path.clone();
179 Box::pin(async move { state.first_not_null(|p| p.try_get_folder_from_path_async(&folder_path)).await })
180 }
181
182 fn try_get_well_known_folder_async(
183 &self,
184 well_known_folder: WellKnownFolder,
185 ) -> LocalBoxFuture<Option<Rc<dyn IStorageFolder>>> {
186 let state = self.state.clone();
187 Box::pin(async move { state.first_not_null(|p| p.try_get_well_known_folder_async(well_known_folder)).await })
188 }
189}