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
use std::ffi::OsStr;

use bstr::{BStr, ByteSlice, ByteVec};

use crate::Spec;

impl std::convert::TryFrom<&OsStr> for Spec {
    type Error = crate::Utf8Error;

    fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
        crate::os_str_into_bstr(value).map(|value| {
            assert_valid_hack(value);
            Spec(value.into())
        })
    }
}

fn assert_valid_hack(input: &BStr) {
    assert!(!input.contains_str(b"/../"));
    assert!(!input.contains_str(b"/./"));
    assert!(!input.starts_with_str(b"../"));
    assert!(!input.starts_with_str(b"./"));
    assert!(!input.starts_with_str(b"/"));
}

impl Spec {
    /// Parse `input` into a `Spec` or `None` if it could not be parsed
    // TODO: tests, actual implementation probably via `git-pathspec` to make use of the crate after all.
    pub fn from_bytes(input: &BStr) -> Option<Self> {
        assert_valid_hack(input);
        Spec(input.into()).into()
    }
    /// Return all paths described by this path spec, using slashes on all platforms.
    pub fn items(&self) -> impl Iterator<Item = &BStr> {
        std::iter::once(self.0.as_bstr())
    }
    /// Adjust this path specification according to the given `prefix`, which may be empty to indicate we are the at work-tree root.
    // TODO: this is a hack, needs test and time to do according to spec. This is just a minimum version to have -something-.
    pub fn apply_prefix(&mut self, prefix: &std::path::Path) -> &Self {
        // many more things we can't handle. `Path` never ends with trailing path separator.
        let prefix = crate::into_bstr(prefix);
        if !prefix.is_empty() {
            let mut prefix = crate::to_unix_separators_on_windows(prefix);
            {
                let path = prefix.to_mut();
                path.push_byte(b'/');
                path.extend_from_slice(&self.0);
            }
            self.0 = prefix.into_owned();
        }
        self
    }
}