Skip to main content

fyrox_ui/file_browser/
filter.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::core::{reflect::prelude::*, visitor::prelude::*};
22use std::fmt::Display;
23use std::ops::Index;
24use std::path::PathBuf;
25use std::{
26    fmt::{Debug, Formatter},
27    path::Path,
28};
29
30#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq)]
31pub struct FileType {
32    pub description: String,
33    pub extension: String,
34}
35
36impl FileType {
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    pub fn new_extension(extension: impl AsRef<str>) -> Self {
42        Self {
43            description: Default::default(),
44            extension: extension.as_ref().to_string(),
45        }
46    }
47
48    pub fn with_description(mut self, description: impl AsRef<str>) -> Self {
49        self.description = description.as_ref().to_string();
50        self
51    }
52
53    pub fn with_extension(mut self, extension: impl AsRef<str>) -> Self {
54        self.extension = extension.as_ref().to_string();
55        self
56    }
57
58    pub fn matches(&self, path: &Path) -> bool {
59        path.extension()
60            .is_some_and(|ext| ext.to_string_lossy() == self.extension)
61    }
62
63    pub fn make_file_name(&self, name: &str) -> PathBuf {
64        let mut file_name = PathBuf::from(name);
65        file_name.set_extension(&self.extension);
66        file_name
67    }
68}
69
70impl Display for FileType {
71    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
72        write!(f, "{} (*.{})", self.description, self.extension)
73    }
74}
75
76#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq)]
77pub struct PathFilter {
78    pub folders_only: bool,
79    pub types: Vec<FileType>,
80}
81
82impl Index<usize> for PathFilter {
83    type Output = FileType;
84
85    fn index(&self, index: usize) -> &Self::Output {
86        self.types.index(index)
87    }
88}
89
90impl PathFilter {
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    pub fn with_file_type(mut self, file_type: FileType) -> Self {
96        self.types.push(file_type);
97        self
98    }
99
100    pub fn folder() -> Self {
101        Self {
102            folders_only: true,
103            types: Default::default(),
104        }
105    }
106
107    pub fn supports_all(&self, path: &Path) -> bool {
108        if self.folders_only {
109            path.is_dir()
110        } else {
111            path.is_dir()
112                || self.is_empty()
113                || self.types.iter().any(|file_type| file_type.matches(path))
114        }
115    }
116
117    pub fn supports_specific_type(&self, path: &Path, index: Option<usize>) -> bool {
118        if self.folders_only {
119            path.is_dir()
120        } else if let Some(index) = index {
121            self.types
122                .get(index)
123                .is_some_and(|file_type| file_type.matches(path))
124        } else {
125            self.is_empty() || self.types.iter().any(|file_type| file_type.matches(path))
126        }
127    }
128
129    pub fn len(&self) -> usize {
130        self.types.len()
131    }
132
133    pub fn is_empty(&self) -> bool {
134        self.types.is_empty()
135    }
136
137    pub fn iter(&self) -> impl Iterator<Item = &FileType> {
138        self.types.iter()
139    }
140
141    pub fn get(&self, i: usize) -> Option<&FileType> {
142        self.types.get(i)
143    }
144}