simpath/
lib.rs

1#![deny(missing_docs)]
2//! Simpath - or Simple Path is a small library for creating, manipulating and using Unix style
3//! `Path`s.
4//!
5//! A `Path` is an environment variable (a String) with one or more directories specified.
6//! They are usually used to find a file that resides in one of the directories.
7//! On most platform the default separator character is `:` but on Windows it is `;`
8//!
9//! If you wish to separate entries with a different separator, it can be modified via API.
10//!
11#[cfg(feature = "urls")]
12extern crate curl;
13#[cfg(feature = "urls")]
14extern crate url;
15
16use std::env;
17use std::fmt;
18use std::fs;
19use std::io::{Error, ErrorKind};
20use std::path::PathBuf;
21
22#[cfg(feature = "urls")]
23use curl::easy::{Handler, WriteError};
24#[cfg(feature = "urls")]
25use url::Url;
26use std::collections::HashSet;
27
28#[cfg(feature = "urls")]
29struct Collector(Vec<u8>);
30
31#[cfg(feature = "urls")]
32impl Handler for Collector {
33    fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
34        self.0.extend_from_slice(data);
35        Ok(data.len())
36    }
37}
38
39// Character used to separate directories in a Path Environment variable on windows is ";"
40#[cfg(target_family = "windows")]
41const DEFAULT_SEPARATOR_CHAR: char = ';';
42// Character used to separate directories in a Path Environment variable on linux/mac/unix is ":"
43#[cfg(not(target_family = "windows"))]
44const DEFAULT_SEPARATOR_CHAR: char = ':';
45
46/// `Simpath` is the struct returned when you create a new on using a named environment variable
47/// which you then use to interact with the `Simpath`
48#[derive(Clone, Debug)]
49pub struct Simpath {
50    separator: char,
51    name: String,
52    directories: HashSet<PathBuf>,
53    #[cfg(feature = "urls")]
54    urls: HashSet<Url>,
55}
56
57/// `FileType` can be used to find an entry in a path of a specific type (`Directory`, `File`, `URL`)
58/// or of `Any` type
59#[derive(Debug, PartialEq)]
60pub enum FileType {
61    /// An entry in the `Simpath` of type `File`
62    File,
63    /// An entry in the `Simpath` of type `Directory`
64    Directory,
65    /// An entry in the `Simpath` of type `Url`
66    Resource,
67    /// An entry in the `Simpath` of `Any` types
68    Any,
69}
70
71/// `FoundType` indicates what type of entry was found
72#[derive(Debug, PartialEq)]
73pub enum FoundType {
74    /// An entry in the `Simpath` of type `File`
75    File(PathBuf),
76    /// An entry in the `Simpath` of type `Directory`
77    Directory(PathBuf),
78    #[cfg(feature = "urls")]
79    /// An entry in the `Simpath` of type `Url`
80    Resource(Url),
81}
82
83/// When validating a `Simpath` there can be the following types of `PathError`s returned
84pub enum PathError {
85    /// The `Path` entry does not exist on the file system
86    DoesNotExist(String),
87    /// The `Path` entry cannot be reads
88    CannotRead(String),
89}
90
91impl Simpath {
92    /// Create a new simpath, providing the name of the environment variable to initialize the
93    /// search path with. If an environment variable of that name exists and it will be parsed
94    /// as a ':' separated list of paths to search. Only paths detected as directories will
95    /// be used, not files.
96    ///
97    /// If an environment variable of that name is *not* found, a new simpath will be created anyway
98    /// and it can have directories added to it programatically and used in the normal fashion to
99    /// search for files
100    ///
101    /// ```
102    /// extern crate simpath;
103    /// use simpath::Simpath;
104    ///
105    /// fn main() {
106    ///     let search_path = Simpath::new("PATH");
107    ///     let ls_file = search_path.find("ls");
108    ///     match ls_file {
109    ///         Ok(found) => println!("'ls' was found at '{:?}'", found),
110    ///         Err(e)   => println!("{}", e)
111    ///     }
112    /// }
113    /// ```
114    ///
115    pub fn new(var_name: &str) -> Self {
116        let mut search_path = Simpath {
117            separator: DEFAULT_SEPARATOR_CHAR,
118            name: var_name.to_string(),
119            directories: HashSet::<PathBuf>::new(),
120            #[cfg(feature = "urls")]
121            urls: HashSet::<Url>::new(),
122        };
123
124        search_path.add_from_env_var(var_name);
125
126        search_path
127    }
128
129    /// Create a new simpath, providing the name of the environment variable to initialize the
130    /// search path with and the separator character for this search path to be used from here on.
131    /// If an environment variable of that name exists and it will be parsed as a list of paths to
132    /// search. Only paths detected as directories will be used, not files.
133    ///
134    /// If an environment variable of that name is *not* found, a new simpath will be created anyway
135    /// and it can have directories added to it programatically and used in the normal fashion to
136    /// search for files.
137    ///
138    /// In all cases, the separator char for this search path will be set to `separator` from here on.
139    ///
140    /// ```
141    /// extern crate simpath;
142    /// use simpath::Simpath;
143    /// use std::env;
144    ///
145    /// fn main() {
146    ///     env::set_var("TEST", "/,.,~");
147    ///     let search_path = Simpath::new("TEST");
148    ///     let two = search_path.find(".");
149    ///     match two {
150    ///         Ok(found) => println!("'.' was found at '{:?}'", found),
151    ///         Err(e)   => println!("{}", e)
152    ///     }
153    /// }
154    /// ```
155    pub fn new_with_separator(var_name: &str, separator: char) -> Self {
156        let mut search_path = Simpath {
157            separator,
158            name: var_name.to_string(),
159            directories: HashSet::<PathBuf>::new(),
160            #[cfg(feature = "urls")]
161            urls: HashSet::<Url>::new(),
162        };
163
164        search_path.add_from_env_var(var_name);
165
166        search_path
167    }
168
169    /// Get the currently set separator character that is used when parsing entries from an environment
170    /// variable
171    pub fn separator(&self) -> char {
172        self.separator
173    }
174
175    /// Get the name associated with the simpath. Note that this could be an empty String
176    /// ```
177    /// extern crate simpath;
178    /// use simpath::Simpath;
179    ///
180    /// fn main() {
181    ///     let search_path = Simpath::new("PATH");
182    ///     println!("Directories in Search Path: {:?}", search_path.name());
183    /// }
184    /// ```
185    pub fn name(&self) -> &str {
186        &self.name
187    }
188
189    /// Get the list of directories that are included in the Search Path
190    ///
191    /// ```
192    /// extern crate simpath;
193    /// use simpath::Simpath;
194    ///
195    /// fn main() {
196    ///     let search_path = Simpath::new("PATH");
197    ///     println!("Directories in Search Path: {:?}", search_path.directories());
198    /// }
199    /// ```
200    pub fn directories(&self) -> &HashSet<PathBuf> {
201        &self.directories
202    }
203
204    #[cfg(feature = "urls")]
205    /// Get the list of URLs that are included in the Search Path
206    ///
207    /// ```
208    /// extern crate simpath;
209    /// use simpath::Simpath;
210    /// use std::env;
211    ///
212    /// fn main() {
213    ///     env::set_var("TEST", "http://ibm.com,https://hp.com");
214    ///     let search_path = Simpath::new("TEST");
215    ///     println!("URLs in Search Path: {:?}", search_path.urls());
216    /// }
217    /// ```
218    pub fn urls(&self) -> &HashSet<Url> {
219        &self.urls
220    }
221
222    /// Try to find a file or resource by name (not full path) on a search path.
223    /// Searching for a file could cause errors, so Result<FoundType, io::Error> is returned
224    /// If it is found `Ok(FoundType)` is returned indicating where the resource/file can be found.
225    /// If it is not found then `Err` is returned.
226    ///
227    /// ```
228    /// extern crate simpath;
229    /// use simpath::Simpath;
230    ///
231    /// fn main() {
232    ///     let search_path = Simpath::new("PATH");
233    ///     match search_path.find("my-file") {
234    ///         Ok(_found_dir) => println!("Didn't expect that!!"),
235    ///         Err(e)         => println!("{}", e.to_string())
236    ///     }
237    /// }
238    /// ```
239    pub fn find(&self, file_name: &str) -> Result<FoundType, Error> {
240        self.find_type(file_name, FileType::Any)
241    }
242
243    /// find an entry of a specific `FileType` in a `Path`
244    ///
245    /// ```
246    /// extern crate simpath;
247    /// use simpath::Simpath;
248    ///
249    /// fn main() {
250    ///     use simpath::FileType;
251    ///     let search_path = Simpath::new("PATH");
252    ///     match search_path.find_type("my-file", FileType::Directory) {
253    ///         Ok(_found_dir) => println!("Didn't expect that!!"),
254    ///         Err(e)         => println!("{}", e.to_string())
255    ///     }
256    /// }
257    /// ```
258    pub fn find_type(&self, file_name: &str, file_type: FileType) -> Result<FoundType, Error> {
259        if file_type == FileType::File || file_type == FileType::Directory || file_type == FileType::Any {
260            for search_dir in &self.directories {
261                for entry in fs::read_dir(search_dir)? {
262                    let file = entry?;
263                    if let Some(filename) = file.file_name().to_str() {
264                        if filename == file_name {
265                            let found_filetype = file.metadata()?.file_type();
266                            match file_type {
267                                FileType::Any => return Ok(FoundType::File(file.path())),
268                                FileType::Directory if found_filetype.is_dir() => return Ok(FoundType::Directory(file.path())),
269                                FileType::File if found_filetype.is_file() || found_filetype.is_symlink() => return Ok(FoundType::File(file.path())),
270                                _ => { /* keep looking */ }
271                            }
272                        }
273                    }
274                }
275            }
276        }
277
278        #[cfg(feature = "urls")]
279            // Look for a URL that ends with '/file_name'
280        if file_type == FileType::Resource || file_type == FileType::Any {
281            for url in &self.urls {
282                let mut segments = url.path_segments()
283                    .ok_or_else(|| Error::new(ErrorKind::NotFound, "Could not get path segments"))?;
284                if segments.next_back() == Some(file_name) {
285                    return Ok(FoundType::Resource(url.clone()));
286                }
287            }
288        }
289
290        Err(Error::new(ErrorKind::NotFound,
291                       format!("Could not find type '{:?}' called '{}' in search path '{}'",
292                               file_type, file_name, self.name)))
293    }
294
295    /// Add an to the search path.
296    ///
297    /// if "urls" feature is enabled:
298    ///     If it parses as as web Url it will be added to the list of
299    ///     base Urls to search, otherwise it will be added to the list of directories to search.
300    /// if "urls" feature is *not* enabled:
301    ///     It is assumed to be a directory and added using `add_directory()`
302    ///
303    /// ```
304    /// extern crate simpath;
305    /// use simpath::Simpath;
306    ///
307    /// fn main() {
308    ///     let mut search_path = Simpath::new("PATH");
309    ///     search_path.add(".");
310    ///
311    /// #[cfg(feature = "urls")]
312    ///     search_path.add("http://ibm.com");
313    ///
314    ///     println!("{}", search_path);
315    /// }
316    /// ```
317    pub fn add(&mut self, entry: &str) {
318        #[cfg(not(feature = "urls"))]
319            self.add_directory(entry);
320
321        #[cfg(feature = "urls")]
322        match Url::parse(entry) {
323            Ok(url) => {
324                match url.scheme() {
325                    #[cfg(feature = "urls")]
326                    "http" | "https" => self.add_url(&url),
327                    "file" => self.add_directory(url.path()),
328                    _ => self.add_directory(entry)
329                }
330            }
331            Err(_) => self.add_directory(entry) /* default to being a directory path */
332        }
333    }
334
335    /// Add a directory to the list of directories to search for files.
336    ///
337    /// ```
338    /// extern crate simpath;
339    /// use simpath::Simpath;
340    ///
341    /// fn main() {
342    ///     let mut search_path = Simpath::new("PATH");
343    ///     search_path.add_directory(".");
344    ///     println!("Directories in Search Path: {:?}", search_path.directories());
345    /// }
346    /// ```
347    pub fn add_directory(&mut self, dir: &str) {
348        self.directories.insert(PathBuf::from(dir));
349    }
350
351    #[cfg(feature = "urls")]
352    /// Add a Url to the list of Base Urls to be used when searching for resources.
353    ///
354    /// ```
355    /// extern crate simpath;
356    /// extern crate url;
357    ///
358    /// use simpath::Simpath;
359    /// use url::Url;
360    ///
361    /// fn main() {
362    ///     let mut search_path = Simpath::new("WEB");
363    ///     search_path.add_url(&Url::parse("http://ibm.com").unwrap());
364    ///     println!("Urls in Search Path: {:?}", search_path.urls());
365    /// }
366    /// ```
367    pub fn add_url(&mut self, url: &Url) {
368        self.urls.insert(url.clone());
369    }
370
371    /// Check if a search path contains an entry
372    ///
373    /// ```
374    /// extern crate simpath;
375    /// use simpath::Simpath;
376    ///
377    /// fn main() {
378    ///     let mut search_path = Simpath::new("FakeEnvVar");
379    ///     if search_path.contains(".") {
380    ///         println!("Well that's a surprise!");
381    ///     }
382    /// }
383    /// ```
384    pub fn contains(&self, entry: &str) -> bool {
385        if self.directories.contains(&PathBuf::from(entry)) {
386            return true;
387        }
388
389        #[cfg(feature = "urls")]
390        if let Ok(url_entry) = Url::parse(entry) {
391            return self.urls.contains(&url_entry);
392        }
393
394        false
395    }
396
397    /// Add entries to the search path, by reading them from an environment variable.
398    ///
399    /// The environment variable should have a set of entries separated by the separator character.
400    /// By default the separator char is `":"` (on non-windows platforms) and `";"` (on windows)
401    /// but it can be modified after creation of search path.
402    ///
403    /// The environment variable is parsed using the separator char set at the time this function
404    /// is called.
405    ///
406    /// To be added each entry must exist and be readable.
407    ///
408    /// ```
409    /// extern crate simpath;
410    /// use simpath::Simpath;
411    ///
412    /// fn main() {
413    ///     let mut search_path = Simpath::new("MyPathName");
414    ///     search_path.add_from_env_var("PATH");
415    ///     if search_path.contains(".") {
416    ///         println!("'.' was in your 'PATH' and has been added to the search path called '{}'",
417    ///                  search_path.name());
418    ///     }
419    /// }
420    /// ```
421    pub fn add_from_env_var(&mut self, var_name: &str) {
422        if let Ok(var_string) = env::var(var_name) {
423            for part in var_string.split(self.separator) {
424                self.add(part);
425            }
426        }
427    }
428
429    /// Add entries to the search path, by reading them from an environment variable.
430    ///
431    /// The environment variable should have a set of entries separated by the specified
432    /// separator character.
433    ///
434    /// To be added each entry must exist and be readable.
435    ///
436    /// NOTE: The separator char is only used while parsing the specified environment variable and
437    /// *does not* modify the separator character in use in the Simpath after this function completes.
438    ///
439    /// ```
440    /// extern crate simpath;
441    /// use simpath::Simpath;
442    /// use std::env;
443    ///
444    /// fn main() {
445    ///     let mut search_path = Simpath::new("MyPathName");
446    ///     env::set_var("TEST", "/,.,~");
447    ///     search_path.add_from_env_var_with_separator("TEST", ',');
448    ///     if search_path.contains(".") {
449    ///         println!("'.' was in your 'TEST' environment variable and has been added to the search path called '{}'",
450    ///                  search_path.name());
451    ///     }
452    /// }
453    /// ```
454    pub fn add_from_env_var_with_separator(&mut self, var_name: &str, separator: char) {
455        if let Ok(var_string) = env::var(var_name) {
456            for part in var_string.split(separator) {
457                self.add_directory(part);
458            }
459        }
460    }
461
462    /// Check if the path is empty, i.e. has no directories added to it, and if the "urls"
463    /// feature is enabled, that is has no urls added to it either.
464    ///
465    /// ```
466    /// extern crate simpath;
467    /// use simpath::Simpath;
468    /// use std::env;
469    ///
470    /// fn main() {
471    ///     let mut search_path = Simpath::new("Foo");
472    ///     assert!(search_path.is_empty(), "The 'Foo' SearchPath should be empty");
473    /// }
474    /// ```
475    pub fn is_empty(&self) -> bool {
476        #[cfg(not(feature = "urls"))]
477        return self.directories.is_empty();
478        #[cfg(feature = "urls")]
479        return self.directories.is_empty() && self.urls.is_empty();
480    }
481}
482
483impl fmt::Display for Simpath {
484    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485        write!(f, "Search Path '{}': Directories: {:?}", self.name, self.directories)?;
486
487        #[cfg(feature = "urls")]
488        write!(f, ", URLs: {:?}", self.urls)?;
489
490        Ok(())
491    }
492}
493
494#[cfg(test)]
495mod test {
496    use std::env;
497    use std::fs;
498    use std::io::Write;
499
500    use super::{DEFAULT_SEPARATOR_CHAR, FileType};
501
502    use super::Simpath;
503
504    #[test]
505    fn can_create() {
506        Simpath::new("PATH");
507    }
508
509    #[test]
510    fn can_create_with_separator() {
511        Simpath::new_with_separator("PATH", ':');
512    }
513
514    #[test]
515    fn name_is_saved() {
516        let path = Simpath::new("MyName");
517        assert_eq!(path.name(), "MyName");
518    }
519
520    #[test]
521    fn find_non_existant_file() {
522        let path = Simpath::new("MyName");
523        assert!(path.find("no_such_file").is_err());
524    }
525
526    #[test]
527    fn display_empty_path() {
528        let path = Simpath::new("MyName");
529        println!("{}", path);
530    }
531
532    #[test]
533    fn directory_is_added() {
534        let mut path = Simpath::new("MyName");
535        assert!(path.directories().is_empty());
536        path.add_directory(&env::current_dir()
537            .expect("Could not get current working directory")
538            .to_string_lossy());
539        let cwd = env::current_dir()
540            .expect("Could not get current working directory").to_string_lossy().to_string();
541        assert!(path.contains(&cwd));
542    }
543
544    #[test]
545    fn cannot_add_same_dir_twice() {
546        let mut path = Simpath::new("MyName");
547        assert!(path.directories().is_empty());
548        path.add_directory(".");
549        path.add_directory(".");
550        assert_eq!(path.directories().len(), 1);
551    }
552
553    #[test]
554    fn find_dir_from_env_variable() {
555        // Create a temp dir for test
556        let temp_dir = tempdir::TempDir::new("simpath").unwrap().into_path();
557        let mut parent_dir = temp_dir.clone();
558        parent_dir.pop();
559
560        // Create a ENV path that includes that dir
561        let var_name = "MyPath";
562        env::set_var(var_name, &parent_dir);
563
564        // create a simpath from the env var
565        let path = Simpath::new(var_name);
566
567        // Check that simpath can find the temp_dir
568        let temp_dir_name = format!("{}.{}",
569                                    temp_dir.file_stem().unwrap().to_str().unwrap(),
570                                    temp_dir.extension().unwrap().to_str().unwrap());
571        assert!(path.find_type(&temp_dir_name, FileType::Directory).is_ok(),
572                "Could not find the simpath temp directory in Path set from env var");
573
574        // clean-up
575        let _ = fs::remove_dir_all(temp_dir);
576    }
577
578    #[test]
579    fn find_file_from_env_variable() {
580        // Create a temp dir for test
581        let temp_dir = tempdir::TempDir::new("simpath").unwrap().into_path();
582
583        // Create a ENV path that includes the path to the temp dir
584        let var_name = "MYPATH";
585        env::set_var(var_name, &temp_dir);
586
587        // create a simpath from the env var
588        let path = Simpath::new(var_name);
589
590        // Create a file in the directory
591        let temp_filename = "testfile";
592        let temp_file_path = format!("{}/{}", temp_dir.display(), temp_filename);
593        let mut file = fs::File::create(&temp_file_path).unwrap();
594        file.write_all(b"test file contents").unwrap();
595
596        // Check that simpath can find the file
597        assert!(path.find_type(temp_filename, FileType::File).is_ok(),
598                "Could not find 'testfile' in Path set from env var");
599
600        // clean-up
601        let _ = fs::remove_dir_all(temp_dir);
602    }
603
604    #[cfg(unix)]
605    #[test]
606    fn find_link_from_env_variable() {
607        // Create a temp dir for test
608        let temp_dir = tempdir::TempDir::new("simpath").unwrap().into_path();
609
610        // Create a ENV path that includes the path to the temp dir
611        let var_name = "MYPATH";
612        env::set_var(var_name, &temp_dir);
613
614        // create a simpath from the env var
615        let path = Simpath::new(var_name);
616
617        // Create a file in the directory
618        let temp_filename = "testfile";
619        let temp_file_path = format!("{}/{}", temp_dir.display(), temp_filename);
620        let mut file = fs::File::create(&temp_file_path).unwrap();
621        file.write_all(b"test file contents").unwrap();
622
623        // Create a link to the file
624        let temp_linkname = "testlink";
625        let temp_link_path = format!("{}/{}", temp_dir.display(), temp_linkname);
626        std::os::unix::fs::symlink(temp_file_path, temp_link_path).expect("Could not create symlink");
627
628        // Check that simpath can find the file
629        assert!(path.find_type(temp_linkname, FileType::File).is_ok(),
630                "Could not find 'testlink' in Path set from env var");
631
632        // clean-up
633        let _ = fs::remove_dir_all(temp_dir);
634    }
635
636    #[test]
637    fn find_dir_using_any_from_env_variable() {
638        // Create a temp dir for test
639        let temp_dir = tempdir::TempDir::new("simpath").unwrap().into_path();
640
641        // Create a ENV path that includes that dir
642        let var_name = "MyPath";
643        env::set_var(var_name, &temp_dir);
644
645        // create a simpath from the env var
646        let path = Simpath::new(var_name);
647
648        // Create a file in the directory
649        let temp_filename = "testfile";
650        let temp_file_path = format!("{}/{}", temp_dir.display(), temp_filename);
651        let mut file = fs::File::create(&temp_file_path).unwrap();
652        file.write_all(b"test file contents").unwrap();
653
654        // Check that simpath can find it
655        assert!(path.find(temp_filename).is_ok(),
656                "Could not find the 'testfile' in Path set from env var");
657
658        // clean-up
659        let _ = fs::remove_dir_all(temp_dir);
660    }
661
662    #[test]
663    fn single_add_from_env_variable() {
664        let var_name = "MyPath";
665        env::set_var(var_name, env::current_dir()
666            .expect("Could not get current working directory")
667            .to_string_lossy().to_string());
668        let path = Simpath::new(var_name);
669        assert!(path.contains(&env::current_dir()
670            .expect("Could not get current working directory").to_string_lossy().to_string()));
671    }
672
673    #[test]
674    fn multiple_add_from_env_variable() {
675        let var_name = "MyPath";
676        env::set_var(var_name, format!("/tmp{}/", DEFAULT_SEPARATOR_CHAR));
677        let path = Simpath::new(var_name);
678        assert!(path.contains("/tmp"));
679        assert!(path.contains("/"));
680    }
681
682    #[test]
683    fn multiple_add_from_env_variable_separator() {
684        let var_name = "MyPath";
685        env::set_var(var_name, "/tmp,/");
686        let path = Simpath::new_with_separator(var_name, ',');
687        assert!(path.contains("/tmp"));
688        assert!(path.contains("/"));
689    }
690
691    #[test]
692    fn display_a_simpath_with_entries() {
693        let var_name = "MyPath";
694        env::set_var(var_name, format!(".{}/", DEFAULT_SEPARATOR_CHAR));
695        let path = Simpath::new(var_name);
696        println!("Simpath can be printed: {}", path);
697    }
698
699    #[cfg(feature = "urls")]
700    mod url_tests {
701        use std::env;
702        use url::Url;
703        use super::super::FileType;
704        use super::Simpath;
705
706        const BASE_URL: &str = "https://www.ibm.com";
707        const EXISTING_RESOURCE: &str = "es-es";
708
709        #[test]
710        fn create_from_env() {
711            let var_name = "MyPath";
712            env::set_var(var_name, BASE_URL);
713            let path = Simpath::new_with_separator(var_name, ',');
714            assert_eq!(path.urls().len(), 1);
715            assert_eq!(path.directories().len(), 0);
716            assert!(path.urls().contains(&Url::parse(BASE_URL)
717                .expect("Could not parse URL")));
718        }
719
720        #[test]
721        fn add_url_that_exists() {
722            let mut path = Simpath::new_with_separator("test", ',');
723            path.add_url(&Url::parse(BASE_URL).expect("Could not parse Url"));
724            assert_eq!(path.urls().len(), 1);
725            assert_eq!(path.directories().len(), 0);
726            assert!(path.urls().contains(&Url::parse(BASE_URL)
727                .expect("Could not parse URL")));
728        }
729
730        #[test]
731        fn cannot_add_same_url_twice() {
732            let mut path = Simpath::new_with_separator("test", ',');
733            path.add_url(&Url::parse(BASE_URL).expect("Could not parse Url"));
734            path.add_url(&Url::parse(BASE_URL).expect("Could not parse Url"));
735            assert_eq!(path.urls().len(), 1);
736            assert_eq!(path.directories().len(), 0);
737            assert!(path.urls().contains(&Url::parse(BASE_URL)
738                .expect("Could not parse URL")));
739        }
740
741        #[test]
742        fn find_resource_not_exist() {
743            let mut search_path = Simpath::new("TEST");
744            search_path.add_url(&Url::parse(BASE_URL).expect("Could not parse Url"));
745            assert!(search_path.find_type("/no-way-this-exists", FileType::Resource).is_err(),
746                    "should not find the resource");
747        }
748
749        #[test]
750        fn find_existing_resource() {
751            let mut search_path = Simpath::new("TEST");
752            search_path.add_url(&Url::parse(BASE_URL).expect("Could not parse Url")
753                .join(EXISTING_RESOURCE).expect("Could not join to Url"));
754            search_path.find_type(EXISTING_RESOURCE, FileType::Resource).expect("Could not find resource");
755        }
756
757        #[test]
758        fn contains_url_that_exists() {
759            let var_name = "MyPath";
760            env::set_var(var_name, BASE_URL);
761            let path = Simpath::new_with_separator(var_name, ',');
762            assert!(path.contains(BASE_URL));
763        }
764
765        #[test]
766        fn display_path_with_directory_and_url() {
767            let var_name = "MyPath";
768            env::set_var(var_name, format!("~,{}", BASE_URL));
769            let path = Simpath::new_with_separator(var_name, ',');
770            println!("{}", path);
771        }
772    }
773}