Skip to main content

xdgkit/
basedir.rs

1/*!
2# Base Dir
3
4The most basic Basedir spec **Linux Only**
5
6The best part is the wrapper around specifics such as icons, desktop directories, trash, etc..
7*/
8
9// basedir.rs
10// Rusified in 2021 Copyright Israel Dahl. All rights reserved.
11// 
12//        /VVVV\
13//      /V      V\
14//    /V          V\
15//   /      0 0     \
16//   \|\|\</\/\>/|/|/
17//        \_/\_/
18// 
19// This program is free software; you can redistribute it and/or modify
20// it under the terms of the GNU General Public License version 2 as
21// published by the Free Software Foundation.
22//
23// This program is distributed in the hope that it will be useful,
24// but WITHOUT ANY WARRANTY; without even the implied warranty of
25// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26// GNU General Public License for more details.
27// 
28// You should have received a copy of the GNU General Public License
29// along with this program; if not, write to the Free Software
30// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
31
32//TODO learn how to namespace this!!!
33use std::env;
34use std::env::VarError;
35use std::path::Path;
36use std::path::PathBuf;
37/// $HOME
38#[allow(dead_code)]
39pub fn home()->Result<String, VarError> {
40    env::var("HOME")
41}
42
43/// the main 'getter' function
44/// This basically checks env::var(whatever) and returns the right results
45fn var_getter(env_var:&str, directory:String)->Result<String, VarError> {
46    match env::var(env_var) {
47        Ok(val)=> Ok(val),
48        Err(_e)=> {
49            // make sure the direcory exists!
50            for path in convert_to_vec(Ok(directory.clone())) {
51                if !Path::new(path.as_str()).is_dir() {
52                    return Err(VarError::NotPresent)
53                }
54            }
55            Ok(directory)
56        },
57    }
58}
59
60/// Redundant $HOME-to-string-path code lives here
61#[allow(dead_code)]
62pub fn prepare_home(directory:&str)->String {
63    let conf_home:Result<String, VarError> = home();
64    if conf_home.is_err() {
65        return String::from("");
66    }
67    let mut path:String = conf_home.ok().unwrap();
68    path.push_str(directory);
69    path
70}
71
72/// Convert ':' list `Result<String, VarError>` into Vec<String>
73#[allow(dead_code)]
74pub fn convert_to_vec(list:Result<String, VarError>)-> Vec<String> {
75    let mut result:Vec<String> = vec![];
76    let input:String = list.unwrap_or_else(|_|"".to_string());
77    if input.is_empty() {
78        return result
79    }
80    let str_res:Vec<&str> = input.split(':').collect();
81    for item in str_res {
82      let s_item:String = item.to_owned();
83      result.push(s_item);
84    }
85    result
86}
87
88/// $XDG_DATA_HOME
89#[allow(dead_code)]
90pub fn data_home()->Result<String, VarError> {
91//$HOME/.local/share
92    let env_var = "XDG_DATA_HOME";
93    let dir = prepare_home("/.local/share");
94    var_getter(env_var, dir)
95}
96
97/// $XDG_CACHE_HOME
98#[allow(dead_code)]
99pub fn cache_home()->Result<String, VarError> {
100    let env_var = "XDG_CACHE_HOME";
101    let dir = prepare_home("/.cache");
102    var_getter(env_var, dir)
103}
104
105/// $XDG_CONFIG_HOME
106#[allow(dead_code)]
107pub fn config_home()->Result<String, VarError> {
108    //$HOME/.config
109    let env_var = "XDG_CONFIG_HOME";
110    let dir = prepare_home("/.config");
111    var_getter(env_var, dir)
112}
113
114/// $XDG_DATA_DIRS
115#[allow(dead_code)]
116pub fn data_dirs()->Result<String, VarError> {
117    let env_var = "XDG_DATA_DIRS";
118    let dir = String::from("/usr/local/share:/usr/share");
119    var_getter(env_var, dir)
120}
121
122/// $XDG_CONFIG_DIRS
123#[allow(dead_code)]
124pub fn config_dirs()->Result<String, VarError> {
125    let env_var = "XDG_CONFIG_DIRS";
126    let dir = String::from("/etc/xdg");
127    var_getter(env_var, dir)
128}
129
130/// user TRASH DIRECTORY
131#[allow(dead_code)]
132pub fn trash()->Result<String, VarError> {
133    if let Ok(mut result) = data_home() {
134        result.push_str("/Trash");
135        return Ok(result)
136    }
137    Ok(String::from(""))
138}
139
140/// Search the XDG data dirs vector for a `filename` in a `directory`
141pub fn search_data_dirs(filename:String, directory:&str) -> String {
142    for dir in data_dirs_vec(directory.to_string()) {
143        let mut tester = dir.to_owned();
144        tester.push('/');
145        tester.push_str(filename.as_str());
146        if Path::new(tester.as_str()).is_file(){
147            return tester.to_owned()
148        }
149    }
150    "".to_string()
151}
152
153/// loop xdg data dirs
154#[allow(dead_code)]
155pub fn loop_data_dirs(directory:String)->Result<String, VarError> {
156    let mut result = String::from("");
157    let mut fail = false;
158    if let Ok(res_list) = data_dirs() {
159        for item in res_list.split(':') {
160            let mut tmp_path:String = item.to_owned();
161            tmp_path.push_str(directory.as_str());
162            //println!("dir:{}",tmp_path.as_str());
163            if Path::new(tmp_path.as_str()).is_dir() {
164                
165                tmp_path.push(':');
166                result.push_str(tmp_path.as_str());
167            }
168        }
169    } else {
170        fail = true;
171    }
172    if let Ok(mut d) = data_home() {
173        d.push_str(directory.as_str());
174        if Path::new(d.as_str()).is_dir() {
175            d.push(':');
176            result.push_str(d.as_str());
177            fail = false;
178        }
179    }
180    if fail {
181        return Err(VarError::NotPresent)
182    }
183    Ok(result)
184}
185pub fn data_dirs_vec(directory:String)->Vec<String> {
186    let mut result:Vec<String> = vec![];
187    if let Ok(res_list) = data_dirs() {
188        for item in res_list.split(':') {
189            let mut tmp_path:String = item.to_owned();
190            tmp_path.push_str(directory.as_str());
191            if Path::new(tmp_path.as_str()).exists() {
192                result.push(tmp_path.to_owned());
193            }
194        }
195    }
196    if let Ok(mut d) = data_home() {
197        d.push_str(directory.as_str());
198        if Path::new(d.as_str()).exists() {
199            result.push(d);
200        }
201    }
202    result.sort();
203    result.dedup();
204    result
205}
206/// loop xdg config dirs
207#[allow(dead_code)]
208pub fn loop_config_dirs(directory:String)->Result<String, VarError> {
209    let mut result = String::from("");
210    let mut fail = false;
211    if let Ok(res_list) = config_dirs() {
212        for item in res_list.split(':') {
213            let mut tmp_path:String = item.to_owned();
214            tmp_path.push_str(directory.as_str());
215            if Path::new(tmp_path.as_str()).is_dir() {
216                tmp_path.push(':');
217                result.push_str(tmp_path.as_str());
218            }
219        }
220    } else {
221        fail = true;
222    }
223    if let Ok(mut d) = config_home() {
224        d.push_str(directory.as_str());
225        if Path::new(d.as_str()).is_dir() {
226            d.push(':');
227            result.push_str(d.as_str());
228            fail = false;
229        }
230    } else {
231        fail = true;
232    }
233    if fail {
234        return Err(VarError::NotPresent)
235    }
236    Ok(result)
237}
238/// The /menu directory
239#[allow(dead_code)]
240pub fn menu()->Result<String, VarError> {
241    loop_config_dirs("/menus".to_string())
242}
243/// The session's menu file, based on `${XDG_MENU_PREFIX}`
244#[allow(dead_code)]
245pub fn session_menu_file()->Option<String> {
246    let menu = match menu() {
247        Ok(menu) => menu,
248        Err(e) => {
249            println!("Error:{}", e);
250            return None;
251        },
252    };
253    let xdg_menu_prefix = match env::var("XDG_MENU_PREFIX") {
254        Ok(prefix) => prefix,
255        Err(_e) => String::from(""),
256    };
257    let app_menu = "applications.menu";
258
259    let str_res:Vec<&str> = menu.split(':').collect();
260    for item in str_res {
261        let mut s_item:String = item.to_owned();
262        s_item.push('/');
263        s_item.push_str(xdg_menu_prefix.as_str());
264        s_item.push_str(app_menu);
265        if Path::new(s_item.as_str()).is_file(){
266            return Some(s_item)
267        }
268    }
269    None
270}
271/// the /menu/applications-merged directory
272#[allow(dead_code)]
273pub fn menu_merged()->Result<String, VarError> {
274    let result = config_dirs();
275    let mut retval:String = "".to_string();
276    if let Ok(res) = result {
277        retval = res;
278        retval.push_str("/menu/applications-merged");
279    }
280    if Path::new(retval.as_str()).is_dir() {
281        return Ok(retval)
282    }
283    Err(VarError::NotPresent)
284}
285
286/// the /applications directories
287#[allow(dead_code)]
288pub fn applications()->Result<String, VarError> {
289    loop_data_dirs("/applications".to_string())
290}
291/// Desktop directories, directories
292#[allow(dead_code)]
293pub fn desktop_directories()->Result<String, VarError> {
294    loop_data_dirs("/desktop-directories".to_string())
295}
296/// Desktop directories, directories vector
297#[allow(dead_code)]
298pub fn desktop_directories_vec()->Vec<String> {
299    data_dirs_vec("/desktop-directories".to_string())
300}
301/// 
302#[allow(dead_code)]
303pub fn autostart()->Result<String, VarError> {
304    loop_config_dirs("/autostart".to_string())
305}
306
307/// Icon directories
308#[allow(dead_code)]
309pub fn icon_dirs()->Result<String, VarError> {
310    let result = home();
311    let mut directories:String = "".to_string();
312    if let Ok(res) = result {
313        directories = res;
314        directories.push_str("/.icons:");
315    }
316    let result = loop_data_dirs("/icons".to_string());
317    if let Ok(res) = result {
318        directories.push_str(&res);
319    }
320    directories.push_str("/usr/share/pixmaps:");
321    Ok(directories)
322}
323/// Vector of Icon directories
324pub fn icon_dirs_vector()->Vec<String> {
325    // make our directory of icons
326    let mut directory_vec:Vec<String> = data_dirs_vec("/icons".to_string());
327
328    if let Ok(mut local_icons) = home() {
329        local_icons.push_str("/.icons");
330        directory_vec.push(local_icons.to_owned());
331    }
332    directory_vec.push("/usr/share/pixmaps".to_string());
333    directory_vec.sort();
334    directory_vec.dedup();
335    directory_vec
336}
337pub fn to_pathbuff(input:Vec<String>) -> Vec<PathBuf> {
338    let mut return_value:Vec<PathBuf> = vec![];
339    for path in input {
340        let p:PathBuf = PathBuf::from(path.as_str());
341        return_value.push(p);
342    }
343    return_value
344}