summaryrefslogtreecommitdiff
path: root/src/path.rs
blob: 5fb58757329d5b4c5a0c8a31ddfc084a0a3e0df3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::env;
use std::error::Error;
use std::path::Path;

/// Manager for a list of "search paths" on the filesystem, for the purpose of
/// resolving the absolute path of data files that have several possible
/// locations. See the documentation of 'find' for more information on the
/// default search paths.
#[derive(Debug)]
pub struct PathManager {
    search: Vec<String>,
}

impl PathManager {
    // TODO: Implement the following additional search paths:
    // - The steam paths on GNU/Linux.
    // - The "standard" paths for OSX (See G_AddSearchPaths)
    // - The "standard" paths for Windows (See G_AddSearchPaths)
    // - Those specified on the command-line.
    //   - See the CommandPaths and CommandGrps globals in common.cpp
    // - The "app dir" on OSX.
    // - PROPERLY add the CWD.
    // - $HOME/apps/rebuild/ (?)
    // - $HOME/.config/rebuild/
    // - Those specified via DUKE3DGRP environment variable.
    //   - See JBF 20031220

    /// Create a new PathManager and initialize the search path list with
    /// acceptable defaults. See the documentation of 'find' for more
    /// information on the default search paths.
    pub fn new() -> PathManager {
        let mut result = PathManager { search: Vec::new() };

        let directories = vec![
            ".",
            "/usr/share/games/jfduke3d",
            "/usr/local/share/games/jfduke3d",
            "/usr/share/games/eduke32",
            "/usr/local/share/games/eduke32",
            "/usr/share/games/rebuild",
            "/usr/local/share/games/rebuild",
        ];

        for directory in directories.iter() {
            result.add_path(directory).ok();
        }

        let directories = vec![
            "$HOME/.rebuild",
        ];

        if let Some(home) = env::home_dir() {
            if let Some(home) = home.to_str() {
                for path in directories.iter() {
                    let path = String::from(*path).replace("$HOME", home);
                    result.add_path(&path).ok();
                }
            }
        }

        result
    }

    /// Add an additional path for searching.
    ///
    /// # Errors
    ///
    /// A return value of 'Err' indicates that the given path did not exist.
    pub fn add_path(&mut self, path: &str) -> Result<(), Box<Error>> {
        if Path::new(&path).exists() {
            self.search.push(String::from(path));
        } else {
            bail!("Path does not exist");
        }

        Ok(())
    }

    /// Go through the search path list in order and return the first existing
    /// path with the the given name, or None if no such file was found. The
    /// default search path order is:
    ///
    /// - ".",
    /// - "/usr/local/share/games/rebuild",
    /// - "/usr/share/games/rebuild",
    /// - "/usr/local/share/games/eduke32",
    /// - "/usr/share/games/eduke32",
    /// - "/usr/local/share/games/jfduke3d",
    /// - "/usr/share/games/jfduke3d",
    /// - "$HOME/.rebuild",
    ///
    /// This is followed by any additional paths in the search path list tha`t
    /// were added by 'add_path'.
    pub fn find(&self, name: &str) -> Option<String> {
        for directory in self.search.iter() {
            let path = format!("{}/{}", directory, name);

            if Path::new(&path).exists() {
                return Some(path);
            }
        }

        None
    }
}