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
use std::path::{Path, PathBuf};

/// A file context specifies where to find files to load.
///
/// When opening an included file, an extended file context is
/// created, to find further included files relative to the file they
/// are inlcuded from.
///
/// # Example
/// ```
/// use rsass::FileContext;
/// use std::path::PathBuf;
///
/// let base = FileContext::new();
/// let (base, file1) =
///     base.file(&PathBuf::from("some").join("dir").join("file.scss"));
/// // base is now a relative to file1, usefull to open files
/// // by paths mentioned in file1.
/// let (base, file2) = base.file("some/other.scss".as_ref());
/// assert_eq!(file1, PathBuf::from("some").join("dir").join("file.scss"));
/// assert_eq!(file2, PathBuf::from("some").join("dir").join("some/other.scss"));
/// ```
#[derive(Clone, Debug)]
pub struct FileContext {
    path: PathBuf,
}

impl FileContext {
    /// Create a new FileContext.
    ///
    /// Files will be resolved from the current working directory.
    pub fn new() -> Self {
        FileContext {
            path: PathBuf::new(),
        }
    }
    /// Get a file from this context.
    ///
    /// Get a path and a FileContext from this FileContext and a path.
    pub fn file(&self, file: &Path) -> (Self, PathBuf) {
        let t = self.path.join(file);
        if let Some(dir) = t.parent() {
            (
                FileContext {
                    path: PathBuf::from(dir),
                },
                t.clone(),
            )
        } else {
            (FileContext::new(), t.clone())
        }
    }

    pub fn find_file(&self, name: &Path) -> Option<(Self, PathBuf)> {
        // TODO Check docs what expansions should be tried!
        // Files with .sass extension needs another parser.
        let parent = name.parent();
        if let Some(name) = name.file_name().and_then(|n| n.to_str()) {
            for name in &[
                name,
                &format!("{}.scss", name),
                &format!("_{}.scss", name),
                &format!("{}/index.scss", name),
                &format!("{}/_index.scss", name),
            ] {
                let full = parent
                    .map(|p| p.join(name))
                    .unwrap_or_else(|| name.into());
                let (c, p) = self.file(&full);
                if p.is_file() {
                    return Some((c, p));
                }
            }
        }
        None
    }
}