shared_mime/mimedb/
mod.rs1use std::{cmp::Ordering, collections::HashMap};
3
4mod build;
5mod query;
6
7use crate::{
8 fnmatch::FileMatcher,
9 search_queue::SearchQueue,
10 strcache::{CachedString, StringCache},
11};
12
13pub struct MimeDB {
15 names: StringCache,
16 type_info: HashMap<CachedString, TypeInfo>,
17 sequence: i32,
18 globs: Vec<GlobRule>,
19}
20
21#[derive(Debug, Clone, Default)]
22struct TypeInfo {
23 description: Option<String>,
24 aliases: Vec<CachedString>,
25 parents: Vec<CachedString>,
26}
27
28#[derive(Debug, Clone)]
29struct GlobRule {
30 matcher: FileMatcher,
31 sequence: i32,
32 weight: i32,
33 mimetype: String,
34}
35
36impl MimeDB {
37 pub fn new() -> MimeDB {
39 MimeDB {
40 names: StringCache::new(),
41 type_info: HashMap::new(),
42 sequence: 0,
43 globs: Vec::new(),
44 }
45 }
46
47 pub fn type_count(&self) -> usize {
49 self.type_info.len()
50 }
51
52 pub fn glob_count(&self) -> usize {
54 self.globs.len()
55 }
56
57 pub fn is_subtype(&self, typ: &str, sup: &str) -> bool {
59 if sup == "application/octet-stream" && !typ.starts_with("inode/") {
61 return true;
62 }
63 let mut queue: SearchQueue<CachedString> = SearchQueue::new();
64 queue.maybe_add(self.names.cache(typ));
65 while let Some(q) = queue.get() {
66 if q == sup {
67 return true;
68 } else if sup == "text/plain" && q.starts_with("text/") {
69 return true;
70 }
71 if let Some(info) = self.type_info.get(&q) {
72 for pt in info.parents.iter() {
73 queue.maybe_add(pt.clone());
74 }
75 }
76 }
77 false
78 }
79
80 pub fn description(&self, typ: &str) -> Option<&str> {
82 self.type_info
83 .get(typ)
84 .map(|ti| ti.description.as_ref())
85 .flatten()
86 .map(|s| s.as_str())
87 }
88
89 pub fn aliases(&self, typ: &str) -> Vec<&str> {
91 if let Some(ti) = self.type_info.get(typ) {
92 ti.aliases.iter().map(|cs| cs.as_ref()).collect()
93 } else {
94 Vec::new()
95 }
96 }
97
98 pub fn parents(&self, typ: &str) -> Vec<&str> {
100 if let Some(ti) = self.type_info.get(typ) {
101 ti.parents.iter().map(|cs| cs.as_ref()).collect()
102 } else {
103 Vec::new()
104 }
105 }
106
107 pub fn supertypes(&self, typ: &str) -> Vec<CachedString> {
111 let mut types = Vec::new();
112 let mut queue: SearchQueue<CachedString> = SearchQueue::new();
113 let mut is_text = false;
114
115 queue.maybe_add(self.names.cache(typ));
117
118 while let Some(qt) = queue.get() {
120 types.push(qt.clone());
122
123 if !is_text && qt.starts_with("text/") {
125 is_text = true;
126 }
127
128 if let Some(info) = self.type_info.get(qt.as_ref()) {
129 for st in info.parents.iter() {
130 queue.maybe_add(st.clone());
131 }
132 }
133 }
134
135 if is_text && !queue.saw("text/plain") {
137 types.push(self.names.cache("text/plain"));
138 }
139 if !typ.starts_with("inode/") && !queue.saw("application/octet-stream") {
140 types.push(self.names.cache("application/octet-stream"));
141 }
142
143 types
144 }
145
146 pub fn compare_types(&self, a: &str, b: &str) -> Ordering {
148 if self.is_subtype(a, b) {
149 Ordering::Less
150 } else if self.is_subtype(b, a) {
151 Ordering::Greater
152 } else {
153 Ordering::Equal
154 }
155 }
156}