1#![allow(clippy::bool_comparison)]
2
3use std::{
4 cmp::Ordering,
5 collections::BTreeMap,
6 fmt, iter,
7 ops::{Deref, DerefMut},
8};
9
10pub const ROOT: &str = "";
12pub const ROOT_OWNED: String = String::new();
13
14pub mod tree_utils {
15 pub const ROOT: &str = "";
16 pub const ROOT_OWNED: String = String::new();
17 pub const DELIMITER: char = '/';
18
19 pub fn parent_path(input: &(impl AsRef<str> + ?Sized)) -> &str {
21 input
22 .as_ref()
23 .rsplit_once(DELIMITER)
24 .map(|(lhs, _rhs)| lhs)
25 .unwrap_or(ROOT)
26 }
27
28 pub fn path_components(input: &(impl AsRef<str> + ?Sized)) -> impl Iterator<Item = &str> {
29 input
30 .as_ref()
31 .split(DELIMITER)
32 .filter(|v| v.is_empty() == false)
33 }
34
35 pub fn file_name(input: &(impl AsRef<str> + ?Sized)) -> &str {
37 input
38 .as_ref()
39 .rsplit_once(DELIMITER)
40 .map(|(_lhs, rhs)| rhs)
41 .unwrap_or_else(|| input.as_ref())
42 }
43
44 pub fn join(input: &(impl AsRef<str> + ?Sized), second: &(impl AsRef<str> + ?Sized)) -> String {
45 let input = input.as_ref();
46 let second = second.as_ref();
47
48 if input == ROOT {
49 second.to_string()
50 } else {
51 format!("{}{}{}", input, DELIMITER, second)
52 }
53 }
54
55 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
57 pub struct TreeHistory(super::UiTree<(), ()>);
58
59 impl TreeHistory {
60 pub fn open_path(
61 &mut self,
62 path: &(impl AsRef<str> + ?Sized),
63 ) -> Result<(), crate::CosmicPathErr> {
64 self.0.add_folder_all(path)
65 }
66
67 pub fn close_path(
68 &mut self,
69 path: &(impl AsRef<str> + ?Sized),
70 ) -> Result<(), crate::CosmicPathErr> {
71 self.0.remove_item(path).map(|_| ())
72 }
73
74 pub fn is_open(&self, path: &(impl AsRef<str> + ?Sized)) -> bool {
75 self.0.item_exists(path)
76 }
77 }
78}
79
80#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
81pub struct UiTree<T, F>(UiTreeFolder<T, F>);
82
83impl<T, F> UiTree<T, F> {
84 pub fn new(root_value: F) -> Self {
85 Self(UiTreeFolder {
86 value: root_value,
87 children: Default::default(),
88 })
89 }
90
91 pub fn iter_paths(&self) -> impl Iterator<Item = (String, UiTreeItemRef<'_, T, F>)> {
92 self.0.iter_paths(ROOT_OWNED).skip(1)
93 }
94
95 pub fn iter(&self) -> impl Iterator<Item = UiTreeItemRef<'_, T, F>> {
96 self.0.iter().skip(1)
97 }
98}
99
100impl<T, F> Deref for UiTree<T, F> {
101 type Target = UiTreeFolder<T, F>;
102
103 fn deref(&self) -> &Self::Target {
104 &self.0
105 }
106}
107
108impl<T, F> DerefMut for UiTree<T, F> {
109 fn deref_mut(&mut self) -> &mut Self::Target {
110 &mut self.0
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
115pub struct UiTreeFolder<T, F = ()> {
116 pub value: F,
117 pub children: BTreeMap<String, Box<UiTreeItem<T, F>>>,
118}
119
120impl<T, F> UiTreeFolder<T, F> {
121 pub fn new(value: F) -> Self {
122 Self {
123 value,
124 children: Default::default(),
125 }
126 }
127
128 pub fn clear(&mut self, value: F) {
130 self.value = value;
131 self.children.clear();
132 }
133
134 pub fn unique_path(
135 &self,
136 folder: &(impl AsRef<str> + ?Sized),
137 base_file_name: &(impl AsRef<str> + ?Sized),
138 ) -> String {
139 let base_file_name = base_file_name.as_ref();
140 let mut path = tree_utils::join(folder, base_file_name);
141 let mut add = 0;
142 while self.item_exists(&path) {
143 add += 1;
144
145 path = tree_utils::join(folder, &format!("{} {}", base_file_name, add));
146 }
147
148 path
149 }
150
151 pub fn file(&self, path: &(impl AsRef<str> + ?Sized)) -> Option<&T> {
152 match self.item(path)? {
153 UiTreeItemRef::Folder(_) => None,
154 UiTreeItemRef::File(file) => Some(file),
155 }
156 }
157
158 pub fn folder(&self, path: &(impl AsRef<str> + ?Sized)) -> Option<&UiTreeFolder<T, F>> {
159 match self.item(path)? {
160 UiTreeItemRef::Folder(folder) => Some(folder),
161 UiTreeItemRef::File(_) => None,
162 }
163 }
164
165 pub fn root(&self) -> UiTreeItemRef<'_, T, F> {
167 UiTreeItemRef::Folder(self)
168 }
169
170 pub fn item(&self, path: &(impl AsRef<str> + ?Sized)) -> Option<UiTreeItemRef<'_, T, F>> {
171 let mut folder = self;
172 let mut iter = tree_utils::path_components(path);
173
174 while let Some(cmp) = iter.next() {
175 match folder.children.get(cmp)?.as_ref() {
176 UiTreeItem::Folder(new_folder) => {
177 folder = new_folder;
178 }
179 UiTreeItem::File(file) => {
180 if iter.next().is_some() {
181 return None;
183 } else {
184 return Some(UiTreeItemRef::File(file));
185 }
186 }
187 };
188 }
189
190 Some(UiTreeItemRef::Folder(folder))
191 }
192
193 pub fn item_mut(
194 &mut self,
195 path: &(impl AsRef<str> + ?Sized),
196 ) -> Option<UiTreeItemRefMut<'_, T, F>> {
197 let mut folder = self;
198 let mut iter = tree_utils::path_components(path);
199
200 while let Some(cmp) = iter.next() {
201 match folder.children.get_mut(cmp)?.as_mut() {
202 UiTreeItem::Folder(new_folder) => {
203 folder = new_folder;
204 }
205 UiTreeItem::File(file) => {
206 if iter.next().is_some() {
207 return None;
209 } else {
210 return Some(UiTreeItemRefMut::File(file));
211 }
212 }
213 };
214 }
215
216 Some(UiTreeItemRefMut::Folder(folder))
217 }
218
219 pub fn file_mut(&mut self, path: &(impl AsRef<str> + ?Sized)) -> Option<&mut T> {
220 match self.item_mut(path)? {
221 UiTreeItemRefMut::Folder(_) => None,
222 UiTreeItemRefMut::File(file) => Some(file),
223 }
224 }
225
226 pub fn folder_mut(
227 &mut self,
228 path: &(impl AsRef<str> + ?Sized),
229 ) -> Option<&mut UiTreeFolder<T, F>> {
230 match self.item_mut(path)? {
231 UiTreeItemRefMut::Folder(folder) => Some(folder),
232 UiTreeItemRefMut::File(_) => None,
233 }
234 }
235
236 pub fn item_exists(&self, path: &(impl AsRef<str> + ?Sized)) -> bool {
237 self.item(path).is_some()
238 }
239
240 pub fn folder_exists(&self, path: &(impl AsRef<str> + ?Sized)) -> bool {
241 self.item(path).map(|v| v.is_folder()).unwrap_or_default()
242 }
243
244 pub fn file_exists(&self, path: &(impl AsRef<str> + ?Sized)) -> bool {
245 self.item(path).map(|v| v.is_file()).unwrap_or_default()
246 }
247
248 pub fn add_item(
249 &mut self,
250 path: &(impl AsRef<str> + ?Sized),
251 new_item: UiTreeItem<T, F>,
252 ) -> Result<(), CosmicPathErr> {
253 let parent_path = tree_utils::parent_path(path);
254 let child_name = tree_utils::file_name(path);
255 if child_name.is_empty() {
256 return Err(CosmicPathErr::EmptyName);
257 }
258
259 let folder = self.item_mut(parent_path).ok_or(CosmicPathErr::BadPath)?;
260 let folder = match folder {
261 UiTreeItemRefMut::Folder(f) => f,
262 UiTreeItemRefMut::File(_) => return Err(CosmicPathErr::BadPath),
263 };
264
265 if folder.children.get(child_name).is_some() {
266 return Err(CosmicPathErr::DuplicateName);
267 }
268
269 folder
270 .children
271 .insert(child_name.to_owned(), Box::new(new_item));
272
273 Ok(())
274 }
275
276 pub fn add_file(
277 &mut self,
278 path: &(impl AsRef<str> + ?Sized),
279 value: T,
280 ) -> Result<(), CosmicPathErr> {
281 self.add_item(path, UiTreeItem::File(value))
282 }
283
284 pub fn add_folder(
285 &mut self,
286 path: &(impl AsRef<str> + ?Sized),
287 folder_value: F,
288 ) -> Result<(), CosmicPathErr> {
289 self.add_item(
290 path,
291 UiTreeItem::Folder(UiTreeFolder {
292 value: folder_value,
293 children: Default::default(),
294 }),
295 )
296 }
297
298 pub fn remove_item(
299 &mut self,
300 path: &(impl AsRef<str> + ?Sized),
301 ) -> Result<UiTreeItem<T, F>, CosmicPathErr> {
302 let parent_path = tree_utils::parent_path(path);
303 let child_name = tree_utils::file_name(path);
304
305 let item = self.item_mut(parent_path).ok_or(CosmicPathErr::BadPath)?;
306 let folder = match item {
307 UiTreeItemRefMut::Folder(f) => f,
308 UiTreeItemRefMut::File(_) => return Err(CosmicPathErr::BadPath),
309 };
310
311 let output = folder
312 .children
313 .remove(child_name)
314 .ok_or(CosmicPathErr::BadPath)?;
315
316 Ok(*output)
317 }
318
319 pub fn remove_file(&mut self, path: &(impl AsRef<str> + ?Sized)) -> Result<T, CosmicPathErr> {
320 if self.file(path).is_none() {
322 return Err(CosmicPathErr::BadPath);
323 }
324
325 match self.remove_item(path)? {
326 UiTreeItem::Folder(_) => unimplemented!(),
327 UiTreeItem::File(f) => Ok(f),
328 }
329 }
330
331 pub fn remove_folder(
332 &mut self,
333 path: &(impl AsRef<str> + ?Sized),
334 ) -> Result<UiTreeFolder<T, F>, CosmicPathErr> {
335 if self.folder(path).is_none() {
336 return Err(CosmicPathErr::BadPath);
337 }
338
339 match self.remove_item(path)? {
340 UiTreeItem::Folder(f) => Ok(f),
341 UiTreeItem::File(_) => unimplemented!(),
342 }
343 }
344
345 pub fn move_item(
346 &mut self,
347 old_path: &(impl AsRef<str> + ?Sized),
348 new_path: &(impl AsRef<str> + ?Sized),
349 ) -> Result<(), CosmicPathErr> {
350 let old_path = old_path.as_ref();
351 let new_path = new_path.as_ref();
352
353 if old_path == new_path {
354 return Ok(());
355 }
356
357 {
359 let parent_path = tree_utils::parent_path(new_path);
360 let child_name = tree_utils::file_name(new_path);
361
362 let new_parent = self.folder(parent_path).ok_or(CosmicPathErr::BadPath)?;
363 if new_parent.children.contains_key(child_name) {
364 return Err(CosmicPathErr::DuplicateName);
365 }
366
367 let mut new_iter = tree_utils::path_components(new_path);
370
371 let mut success = false;
372
373 for old_path in tree_utils::path_components(old_path) {
374 if Some(old_path) != new_iter.next() {
375 success = true;
376 break;
377 }
378 }
379
380 if success == false {
381 return Err(CosmicPathErr::BadPath);
382 }
383
384 let old_path_with_delim = format!("{}{}", old_path, tree_utils::DELIMITER);
385 if new_path.starts_with(&old_path_with_delim) {
386 return Err(CosmicPathErr::BadPath);
387 }
388 }
389
390 let node = self.remove_item(old_path)?;
392
393 self.add_item(new_path, node).unwrap();
395
396 Ok(())
397 }
398
399 pub fn files(&self) -> impl Iterator<Item = &'_ T> {
400 self.children.values().flat_map(|n| {
401 let x: Box<dyn Iterator<Item = &T>> = match n.as_ref() {
402 UiTreeItem::Folder(f) => Box::new(f.files()),
403 UiTreeItem::File(f) => Box::new(iter::once(f)),
404 };
405
406 x
407 })
408 }
409
410 pub fn files_mut(&mut self) -> impl Iterator<Item = &'_ mut T> {
411 self.children.values_mut().flat_map(|n| {
412 let x: Box<dyn Iterator<Item = &mut T>> = match n.as_mut() {
413 UiTreeItem::Folder(f) => Box::new(f.files_mut()),
414 UiTreeItem::File(f) => Box::new(iter::once(f)),
415 };
416
417 x
418 })
419 }
420
421 pub fn files_paths(&self, base_path: String) -> impl Iterator<Item = (String, &'_ T)> {
422 self.children.iter().flat_map(move |(subpath, n)| {
423 let path = tree_utils::join(&base_path, subpath);
424 let x: Box<dyn Iterator<Item = (String, &T)>> = match n.as_ref() {
425 UiTreeItem::Folder(f) => Box::new(f.files_paths(path)),
426 UiTreeItem::File(f) => Box::new(iter::once((path, f))),
427 };
428
429 x
430 })
431 }
432
433 pub fn files_paths_mut(
434 &mut self,
435 base_path: String,
436 ) -> impl Iterator<Item = (String, &'_ mut T)> {
437 self.children.iter_mut().flat_map(move |(subpath, n)| {
438 let path = tree_utils::join(&base_path, subpath);
439 let x: Box<dyn Iterator<Item = (String, &mut T)>> = match n.as_mut() {
440 UiTreeItem::Folder(f) => Box::new(f.files_paths_mut(path)),
441 UiTreeItem::File(f) => Box::new(iter::once((path, f))),
442 };
443
444 x
445 })
446 }
447
448 pub fn set_children(&mut self, children: BTreeMap<String, Box<UiTreeItem<T, F>>>) {
450 self.children = children;
451 }
452
453 pub fn iter(&self) -> impl Iterator<Item = UiTreeItemRef<'_, T, F>> {
454 iter::once(UiTreeItemRef::Folder(self)).chain(self.children.iter().flat_map(
455 move |(_subpath, item)| {
456 let x: Box<dyn Iterator<Item = UiTreeItemRef<'_, T, F>>> = match item.as_ref() {
457 UiTreeItem::Folder(f) => Box::new(f.iter()),
458 UiTreeItem::File(f) => Box::new(iter::once(UiTreeItemRef::File(f))),
459 };
460
461 x
462 },
463 ))
464 }
465
466 pub fn iter_paths(
467 &self,
468 base: String,
469 ) -> impl Iterator<Item = (String, UiTreeItemRef<'_, T, F>)> {
470 iter::once((base.clone(), UiTreeItemRef::Folder(self))).chain(
471 self.children.iter().flat_map(move |(subpath, item)| {
472 let path = tree_utils::join(&base, subpath);
473
474 let x: Box<dyn Iterator<Item = (String, UiTreeItemRef<'_, T, F>)>> =
475 match item.as_ref() {
476 UiTreeItem::Folder(f) => Box::new(f.iter_paths(path)),
477 UiTreeItem::File(f) => Box::new(iter::once((path, UiTreeItemRef::File(f)))),
478 };
479
480 x
481 }),
482 )
483 }
484
485 pub fn for_each_mut<R>(
487 &mut self,
488 empty_folders_pass: bool,
489 filter: impl Fn(&str, &T) -> bool + Copy,
490 mut on_item: impl FnMut(&str, &mut UiTreeItemRefMut<'_, T, F>) -> Option<R>,
491 ) {
492 fn observer<T, F, R>(
493 full_path: &str,
494 input: &mut BTreeMap<String, Box<UiTreeItem<T, F>>>,
495 filter: impl Fn(&str, &T) -> bool + Copy,
496 empty_folders_pass: bool,
497 on_item: &mut impl FnMut(&str, &mut UiTreeItemRefMut<'_, T, F>) -> Option<R>,
498 ) {
499 let intermediary_sort = {
500 let mut intermediary_sort: Vec<_> = input.iter_mut().collect();
501 intermediary_sort.sort_unstable_by(|lhs, rhs| {
502 if lhs.1.is_folder() && rhs.1.is_file() {
503 Ordering::Less
504 } else if rhs.1.is_folder() {
505 Ordering::Greater
506 } else {
507 let lhs = ListSorter::new(lhs.0);
508 let rhs = ListSorter::new(rhs.0);
509
510 lhs.cmp(&rhs)
511 }
512 });
513 intermediary_sort
514 };
515
516 for (new_name, child) in intermediary_sort {
517 let new_child_full_path = tree_utils::join(full_path, new_name);
518
519 let passed = match child.to_reference() {
520 UiTreeItemRef::Folder(folder) => {
521 let mut found_file = false;
522 let mut output = folder
523 .iter_paths(new_child_full_path.to_string())
524 .filter_map(|(path, v)| match v {
525 UiTreeItemRef::Folder(_) => None,
526 UiTreeItemRef::File(file_data) => {
527 found_file = true;
528 Some((path, file_data))
529 }
530 })
531 .any(|(path, v)| filter(&path, v));
532
533 if output == false && found_file == false && empty_folders_pass {
534 output = true;
535 }
536
537 output
538 }
539 UiTreeItemRef::File(file) => filter(&new_child_full_path, file),
540 };
541 if passed {
542 let mut child = child.to_reference_mut();
543 if let Some(_inner) = on_item(&new_child_full_path, &mut child) {
544 if let UiTreeItemRefMut::Folder(folder) = child {
545 observer(
546 &new_child_full_path,
547 &mut folder.children,
548 filter,
549 empty_folders_pass,
550 on_item,
551 );
552 }
553 }
554 }
555 }
556 }
557
558 observer(
559 tree_utils::ROOT,
560 &mut self.children,
561 filter,
562 empty_folders_pass,
563 &mut on_item,
564 );
565 }
566
567 pub fn for_each<R>(
569 &self,
570 empty_folders_pass: bool,
571 filter: impl Fn(&str, &T) -> bool + Copy,
572 mut on_item: impl FnMut(&str, &UiTreeItemRef<'_, T, F>) -> Option<R>,
573 ) {
574 fn observer<T, F, R>(
575 full_path: &str,
576 input: &BTreeMap<String, Box<UiTreeItem<T, F>>>,
577 filter: impl Fn(&str, &T) -> bool + Copy,
578 empty_folders_pass: bool,
579 on_item: &mut impl FnMut(&str, &UiTreeItemRef<'_, T, F>) -> Option<R>,
580 ) {
581 let intermediary_sort = {
582 let mut intermediary_sort: Vec<_> = input.iter().collect();
583 intermediary_sort.sort_unstable_by(|lhs, rhs| {
584 if lhs.1.is_folder() && rhs.1.is_file() {
585 Ordering::Less
586 } else if rhs.1.is_folder() {
587 Ordering::Greater
588 } else {
589 let lhs = ListSorter::new(lhs.0);
590 let rhs = ListSorter::new(rhs.0);
591
592 lhs.cmp(&rhs)
593 }
594 });
595 intermediary_sort
596 };
597
598 for (new_name, child) in intermediary_sort {
599 let new_child_full_path = tree_utils::join(full_path, new_name);
600
601 let passed = match child.to_reference() {
602 UiTreeItemRef::Folder(folder) => {
603 let mut found_file = false;
604 let mut output = folder
605 .iter_paths(new_child_full_path.to_string())
606 .filter_map(|(path, v)| match v {
607 UiTreeItemRef::Folder(_) => None,
608 UiTreeItemRef::File(file_data) => {
609 found_file = true;
610 Some((path, file_data))
611 }
612 })
613 .any(|(path, v)| filter(&path, v));
614
615 if output == false && found_file == false && empty_folders_pass {
616 output = true;
617 }
618
619 output
620 }
621 UiTreeItemRef::File(file) => filter(&new_child_full_path, file),
622 };
623 if passed {
624 let child = child.to_reference();
625 if let Some(_inner) = on_item(&new_child_full_path, &child) {
626 if let UiTreeItemRef::Folder(folder) = child {
627 observer(
628 &new_child_full_path,
629 &folder.children,
630 filter,
631 empty_folders_pass,
632 on_item,
633 );
634 }
635 }
636 }
637 }
638 }
639
640 observer(
641 tree_utils::ROOT,
642 &self.children,
643 filter,
644 empty_folders_pass,
645 &mut on_item,
646 );
647 }
648}
649
650impl<T, F> UiTreeFolder<T, F>
651where
652 F: Default,
653{
654 pub fn add_folder_all(
655 &mut self,
656 path: &(impl AsRef<str> + ?Sized),
657 ) -> Result<(), CosmicPathErr> {
658 let mut folder = self;
659
660 for component in tree_utils::path_components(path) {
661 if let Err(e) = folder.add_folder(component, F::default()) {
662 match e {
663 CosmicPathErr::BadPath
664 | CosmicPathErr::EmptyName
665 | CosmicPathErr::EmptySubpath => {
666 return Err(e);
667 }
668 CosmicPathErr::DuplicateName => {}
670 }
671 }
672
673 folder = folder.folder_mut(component).unwrap();
674 }
675
676 Ok(())
677 }
678}
679
680#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
681pub enum UiTreeItem<T, F> {
682 Folder(UiTreeFolder<T, F>),
683 File(T),
684}
685
686impl<T, F> UiTreeItem<T, F> {
687 pub fn to_reference(&self) -> UiTreeItemRef<'_, T, F> {
688 match self {
689 UiTreeItem::Folder(folder) => UiTreeItemRef::Folder(folder),
690 UiTreeItem::File(file) => UiTreeItemRef::File(file),
691 }
692 }
693
694 pub fn to_reference_mut(&mut self) -> UiTreeItemRefMut<'_, T, F> {
695 match self {
696 UiTreeItem::Folder(folder) => UiTreeItemRefMut::Folder(folder),
697 UiTreeItem::File(file) => UiTreeItemRefMut::File(file),
698 }
699 }
700
701 pub fn is_folder(&self) -> bool {
703 matches!(self, Self::Folder(..))
704 }
705
706 pub fn as_folder(&self) -> Option<&UiTreeFolder<T, F>> {
708 if let UiTreeItem::Folder(v) = self {
709 Some(v)
710 } else {
711 None
712 }
713 }
714
715 pub fn as_folder_mut(&mut self) -> Option<&mut UiTreeFolder<T, F>> {
717 if let UiTreeItem::Folder(v) = self {
718 Some(v)
719 } else {
720 None
721 }
722 }
723
724 pub fn as_file(&self) -> Option<&T> {
726 if let UiTreeItem::File(v) = self {
727 Some(v)
728 } else {
729 None
730 }
731 }
732
733 pub fn is_file(&self) -> bool {
735 matches!(self, Self::File(..))
736 }
737}
738
739impl<T> UiTreeItem<T, T> {
740 pub fn data(&self) -> &T {
741 self.to_reference().data()
742 }
743
744 pub fn data_mut(&mut self) -> &mut T {
745 self.to_reference_mut().data_mut()
746 }
747
748 pub fn data_owned(self) -> T {
749 match self {
750 UiTreeItem::Folder(t) => t.value,
751 UiTreeItem::File(t) => t,
752 }
753 }
754}
755
756#[derive(Debug, PartialEq, Eq)]
757pub enum UiTreeItemRef<'a, T, F> {
758 Folder(&'a UiTreeFolder<T, F>),
759 File(&'a T),
760}
761
762impl<'a, T, F> Clone for UiTreeItemRef<'a, T, F> {
763 fn clone(&self) -> Self {
764 match self {
765 Self::Folder(arg0) => Self::Folder(arg0),
766 Self::File(arg0) => Self::File(arg0),
767 }
768 }
769}
770impl<'a, T, F> Copy for UiTreeItemRef<'a, T, F> {}
771
772impl<'a, T, F> UiTreeItemRef<'a, T, F> {
773 pub fn is_folder(&self) -> bool {
775 matches!(self, Self::Folder(..))
776 }
777
778 pub fn is_file(&self) -> bool {
780 matches!(self, Self::File(..))
781 }
782
783 pub fn as_file(self) -> Option<&'a T> {
785 if let Self::File(v) = self {
786 Some(v)
787 } else {
788 None
789 }
790 }
791}
792
793impl<'a, T> UiTreeItemRef<'a, T, T> {
795 pub fn data(self) -> &'a T {
796 match self {
797 UiTreeItemRef::Folder(t) => &t.value,
798 UiTreeItemRef::File(t) => t,
799 }
800 }
801}
802
803impl<'a, T, F> UiTreeItemRef<'a, T, F>
804where
805 T: Clone,
806 F: Clone,
807{
808 pub fn to_owned(&self) -> UiTreeItem<T, F> {
809 match *self {
810 UiTreeItemRef::Folder(f) => UiTreeItem::Folder(f.clone()),
811 UiTreeItemRef::File(f) => UiTreeItem::File(f.clone()),
812 }
813 }
814}
815
816#[derive(Debug)]
817pub enum UiTreeItemRefMut<'a, T, F> {
818 Folder(&'a mut UiTreeFolder<T, F>),
819 File(&'a mut T),
820}
821
822impl<'a, T, F> UiTreeItemRefMut<'a, T, F> {
823 pub fn is_folder(&self) -> bool {
825 matches!(self, Self::Folder(..))
826 }
827
828 pub fn is_file(&self) -> bool {
830 matches!(self, Self::File(..))
831 }
832}
833
834impl<'a, T> UiTreeItemRefMut<'a, T, T> {
836 pub fn data_mut(self) -> &'a mut T {
837 match self {
838 UiTreeItemRefMut::Folder(t) => &mut t.value,
839 UiTreeItemRefMut::File(t) => t,
840 }
841 }
842}
843
844#[derive(Debug, PartialEq, Eq, Clone, Copy)]
846pub enum CosmicPathErr {
847 BadPath,
848 EmptyName,
849 EmptySubpath,
850 DuplicateName,
851}
852
853impl fmt::Display for CosmicPathErr {
854 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
855 let st = match self {
856 CosmicPathErr::BadPath => "Path was not valid.",
857 CosmicPathErr::DuplicateName => {
858 "Attempted to add a new node with the same name as an existing node"
859 }
860 CosmicPathErr::EmptySubpath => {
861 "Attempted to add a chid with the empty subpath \"\" as part of its path."
862 }
863 CosmicPathErr::EmptyName => {
864 "An empty name is never a valid name for a file or a folder, except the ROOT."
865 }
866 };
867
868 f.pad(st)
869 }
870}
871impl std::error::Error for CosmicPathErr {}
872
873pub struct LeafTreeFoldersHaveNoData;
875
876#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
877#[serde(transparent)]
878#[repr(transparent)]
879pub struct LeafTree<T>(UiTree<T, ()>);
880
881impl<T> Deref for LeafTree<T> {
882 type Target = UiTree<T, ()>;
883
884 fn deref(&self) -> &Self::Target {
885 &self.0
886 }
887}
888
889impl<T> DerefMut for LeafTree<T> {
890 fn deref_mut(&mut self) -> &mut Self::Target {
891 &mut self.0
892 }
893}
894
895impl<T> LeafTree<T> {
896 pub fn new() -> Self {
897 Self(UiTree::new(()))
898 }
899
900 pub fn add_folder(&mut self, path: &(impl AsRef<str> + ?Sized)) -> Result<(), CosmicPathErr> {
904 self.0.add_folder(path, ())
905 }
906
907 pub fn iter(&self) -> impl Iterator<Item = (String, UiTreeItemRef<'_, T, ()>)> {
910 self.0.iter_paths()
911 }
912
913 pub fn files_paths(&self) -> impl Iterator<Item = (String, &T)> {
916 self.0.files_paths(tree_utils::ROOT_OWNED)
917 }
918
919 pub fn clear(&mut self) {
921 self.0.clear(());
922 }
923}
924
925impl<T> Default for LeafTree<T> {
926 fn default() -> Self {
927 Self::new()
928 }
929}
930
931impl<Tree> FromIterator<(String, Tree)> for LeafTree<Tree> {
932 fn from_iter<T: IntoIterator<Item = (String, Tree)>>(iter: T) -> Self {
933 let mut me = Self::new();
934
935 for (path, v) in iter {
936 let parent_path = tree_utils::parent_path(&path);
937
938 me.add_folder_all(parent_path).unwrap();
939 me.add_file(&path, v).unwrap();
940 }
941
942 me
943 }
944}
945
946#[derive(Debug, PartialEq, Eq)]
947struct ListSorter<'a>(Option<i32>, &'a str);
948
949impl<'a> ListSorter<'a> {
950 fn new(input: &'a str) -> Self {
951 match input.split_once('.') {
952 Some((lhs, rhs)) => match lhs.parse::<i32>().ok() {
953 Some(v) => Self(Some(v), rhs),
954 None => Self(None, input),
955 },
956 None => Self(None, input),
957 }
958 }
959}
960
961impl<'a> PartialOrd for ListSorter<'a> {
962 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
963 Some(self.cmp(other))
964 }
965}
966
967impl<'a> Ord for ListSorter<'a> {
968 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
969 match (&self.0, &other.0) {
970 (Some(my_num), Some(their_num)) => {
971 let ord = my_num.cmp(their_num);
972 if ord != Ordering::Equal {
973 return ord;
974 } else {
975 }
977 }
978 (Some(_), None) => {
979 return Ordering::Less;
980 }
981 (None, Some(_)) => {
982 return Ordering::Greater;
983 }
984 (None, None) => {
985 }
987 }
988
989 self.1.cmp(self.1)
990 }
991}
992
993#[cfg(test)]
994mod cosmic_tree_tests {
995 use super::*;
996
997 #[test]
998 fn add_children() {
999 let mut tree = LeafTree::new();
1000
1001 tree.add_folder("bean_zero").unwrap();
1002 tree.add_file("bean_one", 1).unwrap();
1003
1004 assert!(tree.folder("bean_zero").is_some());
1005 assert_eq!(*tree.file("bean_one").unwrap(), 1);
1006
1007 tree.add_file("bean_zero/bean_three", 3).unwrap();
1008 assert!(tree.item_exists("bean_zero/bean_three"));
1009 assert_eq!(*tree.file("bean_zero/bean_three").unwrap(), 3);
1010
1011 assert!(tree.item_exists(ROOT));
1013 assert!(tree.item_exists(""));
1014
1015 assert_eq!(
1017 tree.add_file("bean_zero", 0).unwrap_err(),
1018 CosmicPathErr::DuplicateName
1019 );
1020
1021 assert_eq!(
1023 tree.add_file("bean_zero/bean_three", 3).unwrap_err(),
1024 CosmicPathErr::DuplicateName
1025 );
1026
1027 assert_eq!(
1029 tree.add_file("bean_zero/bean_four/bean_three", 5)
1030 .unwrap_err(),
1031 CosmicPathErr::BadPath
1032 );
1033 assert_eq!(tree.add_file("", 5).unwrap_err(), CosmicPathErr::EmptyName);
1035
1036 assert_eq!(tree.add_file("", 10).unwrap_err(), CosmicPathErr::EmptyName);
1038 assert_eq!(
1039 tree.add_file("bean_zero", 10).unwrap_err(),
1040 CosmicPathErr::DuplicateName
1041 );
1042 assert_eq!(
1043 tree.add_file("bean_zero/", 10).unwrap_err(),
1044 CosmicPathErr::EmptyName
1045 );
1046 }
1047
1048 #[test]
1049 fn remove_children() {
1050 let mut tree = LeafTree::new();
1051
1052 tree.add_file("doomed_bean", 0).unwrap();
1054 tree.remove_file("doomed_bean").unwrap();
1055 assert!(!tree.item_exists("doomed_bean"));
1056
1057 tree.add_folder("bean_zero").unwrap();
1059 tree.add_file("bean_zero/bean_child", 1).unwrap();
1060
1061 assert_eq!(*tree.file("bean_zero/bean_child").unwrap(), 1);
1062
1063 assert!(tree.item_exists("bean_zero/bean_child"));
1064 assert!(!tree.item_exists("bean_zero/fake_bongo_child"));
1065
1066 assert_eq!(tree.remove_file("bean_zero/bean_child").unwrap(), 1);
1067
1068 assert!(!tree.item_exists("bean_zero/bean_child"));
1070 assert!(!tree.item_exists("bean_child"));
1072
1073 tree.add_folder("grandma").unwrap();
1075 tree.add_folder("grandma/mum").unwrap();
1076 tree.add_file("grandma/mum/kid", 2).unwrap();
1077
1078 let egg = tree.remove_folder("grandma").unwrap();
1080
1081 assert!(!tree.item_exists("grandma/mum/kid"));
1083 assert!(!tree.item_exists("grandma/mum"));
1084 assert!(!tree.item_exists("grandma"));
1085
1086 assert!(egg.item_exists(""));
1088 assert!(egg.item_exists("mum"));
1089 assert!(egg.item_exists("mum/kid"));
1090 }
1091
1092 #[test]
1093 fn move_children() {
1094 let mut tree = LeafTree::new();
1095
1096 tree.add_folder("a").unwrap();
1098 tree.add_file("a/b", 1).unwrap();
1099 tree.add_file("a/duck", 2).unwrap();
1101 tree.move_item("a/b", "b").unwrap();
1103 assert!(!tree.item_exists("a/b"));
1105 assert!(tree.item_exists("b"));
1107 assert!(tree.item_exists("a/duck"));
1109
1110 assert_eq!(tree.children.len(), 2);
1114
1115 tree.add_folder("ducky_dad").unwrap();
1117 tree.move_item("a/duck", "ducky_dad/duck").unwrap();
1118
1119 assert!(tree.item_exists("ducky_dad/duck"));
1121 assert_eq!(*tree.file("ducky_dad/duck").unwrap(), 2);
1122
1123 tree.move_item("ducky_dad/duck", "ducky_dad/ducky_child")
1125 .unwrap();
1126 assert!(tree.item_exists("ducky_dad/ducky_child"));
1127 assert!(!tree.item_exists("ducky_dad/duck"));
1128 assert_eq!(*tree.file("ducky_dad/ducky_child").unwrap(), 2);
1129
1130 tree.add_folder("grandma").unwrap();
1132 tree.add_folder("grandma/mum").unwrap();
1133 tree.add_file("grandma/mum/kid", 2).unwrap();
1134
1135 tree.add_folder("cool_place").unwrap();
1136 tree.move_item("grandma/mum", "cool_place/mum").unwrap();
1137
1138 assert!(tree.item_exists("cool_place/mum"));
1139 assert!(tree.item_exists("cool_place/mum/kid"));
1140 assert!(!tree.item_exists("grandma/mum/kid"));
1141
1142 assert_eq!(tree.children.len(), 5);
1146
1147 tree.clear();
1148
1149 tree.add_folder("a").unwrap();
1150 tree.add_folder("a/b").unwrap();
1151 tree.add_folder("a/b/c").unwrap();
1152
1153 assert_eq!(
1154 tree.move_item("a/b", "a/b/c/bad").unwrap_err(),
1155 CosmicPathErr::BadPath
1156 );
1157 }
1158
1159 #[test]
1160 fn iter() {
1161 let mut tree = LeafTree::new();
1162 tree.add_folder("a").unwrap();
1163 tree.add_folder("a/b").unwrap();
1164 tree.add_file("a/b/c", 0).unwrap();
1165 tree.add_folder("a/b/d").unwrap();
1166 tree.add_file("a/b/d/e", 0).unwrap();
1167 tree.add_file("a/b/f", 1).unwrap();
1168 tree.add_file("a/g", 2).unwrap();
1169 tree.add_file("a/h", 3).unwrap();
1170 tree.add_file("i", 4).unwrap();
1171
1172 let values: Vec<_> = tree.files().copied().collect();
1173 assert_eq!(values, [0, 0, 1, 2, 3, 4]);
1174 }
1175
1176 #[test]
1177 fn iter_mut() {
1178 let mut tree = LeafTree::new();
1179 tree.add_folder("a").unwrap();
1180 tree.add_folder("a/b").unwrap();
1181 tree.add_file("a/b/c", 0).unwrap();
1182 tree.add_folder("a/b/d").unwrap();
1183 tree.add_file("a/b/d/e", 0).unwrap();
1184 tree.add_file("a/b/f", 1).unwrap();
1185 tree.add_file("a/g", 2).unwrap();
1186 tree.add_file("a/h", 3).unwrap();
1187 tree.add_file("i", 4).unwrap();
1188
1189 {
1190 let mut iterator = tree.files_mut();
1191
1192 assert_eq!(*iterator.next().unwrap(), 0);
1193 assert_eq!(*iterator.next().unwrap(), 0);
1194 assert_eq!(*iterator.next().unwrap(), 1);
1195 assert_eq!(*iterator.next().unwrap(), 2);
1196 assert_eq!(*iterator.next().unwrap(), 3);
1197 assert_eq!(*iterator.next().unwrap(), 4);
1198 assert_eq!(iterator.next(), None);
1199 }
1200
1201 {
1202 let mut iterator = tree.iter();
1203
1204 assert_eq!(iterator.next().unwrap().0, "a");
1206 assert_eq!(iterator.next().unwrap().0, "a/b");
1207 assert_eq!(iterator.next().unwrap().0, "a/b/c");
1208 assert_eq!(iterator.next().unwrap().0, "a/b/d");
1209 assert_eq!(iterator.next().unwrap().0, "a/b/d/e");
1210 assert_eq!(iterator.next().unwrap().0, "a/b/f");
1211 assert_eq!(iterator.next().unwrap().0, "a/g");
1212 assert_eq!(iterator.next().unwrap().0, "a/h");
1213 assert_eq!(iterator.next().unwrap().0, "i");
1214 assert_eq!(iterator.next(), None);
1215 }
1216 }
1217}