summaryrefslogtreecommitdiffstats
path: root/src/tree_build/bline.rs
blob: 3eefdbcab3e1e4547776db650a5342a7cc8aa9e1 (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use {
    super::bid::BId,
    crate::{
    app::AppContext,
        errors::TreeBuildError,
        git::GitIgnoreChain,
        tree::*,
    },
    id_arena::Arena,
    std::{fs, path::PathBuf, result::Result},
};

/// like a tree line, but with the info needed during the build
/// This structure isn't usable independantly from the tree builder
pub struct BLine {
    pub parent_id: Option<BId>,
    pub path: PathBuf,
    pub depth: u16,
    pub subpath: String,
    pub name: String,
    pub file_type: fs::FileType,
    pub children: Option<Vec<BId>>, // sorted and filtered
    pub next_child_idx: usize,      // index for iteration, among the children
    pub has_error: bool,
    pub has_match: bool,
    pub direct_match: bool,
    pub score: i32,
    pub nb_kept_children: i32, // used during the trimming step
    pub git_ignore_chain: GitIgnoreChain,
    pub special_handling: SpecialHandling,
}

impl BLine {
    /// a special constructor, checking nothing
    pub fn from_root(
        blines: &mut Arena<BLine>,
        path: PathBuf,
        git_ignore_chain: GitIgnoreChain,
        _options: &TreeOptions,
    ) -> Result<BId, TreeBuildError> {
        let name = match path.file_name() {
            Some(name) => name.to_string_lossy().to_string(),
            None => String::from("???"), // should not happen
        };
        if let Ok(md) = fs::metadata(&path) {
            let file_type = md.file_type();
            Ok(blines.alloc(BLine {
                parent_id: None,
                path,
                depth: 0,
                name,
                subpath: String::new(),
                children: None,
                next_child_idx: 0,
                file_type,
                has_error: false,
                has_match: true,
                direct_match: false,
                score: 0,
                nb_kept_children: 0,
                git_ignore_chain,
                special_handling: SpecialHandling::None,
            }))
        } else {
            Err(TreeBuildError::FileNotFound {
                path: format!("{:?}", path),
            })
        }
    }
    /// tell whether we should list the childs of the present line
    pub fn can_enter(&self) -> bool {
        if self.file_type.is_dir() && self.special_handling != SpecialHandling::NoEnter {
            return true;
        }
        if self.special_handling == SpecialHandling::Enter {
            // we must chek we're a link to a directory
            if self.file_type.is_symlink() {
                if let Ok(target) = fs::read_link(&self.path) {
                    let mut target_path = PathBuf::from(&target);
                    if target_path.is_relative() {
                        target_path = self.path.parent().unwrap().join(target_path)
                    }
                    if let Ok(target_metadata) = fs::symlink_metadata(&target_path) {
                        if target_metadata.file_type().is_dir() {
                            if self.path.starts_with(target_path) {
                                debug!("not entering link because it's a parent"); // lets's not cycle
                            } else {
                                debug!("entering {:?} because of special path rule", &self.path);
                                return true;
                            }
                        }
                    }
                }
            }
        }
        false
    }
    pub fn to_tree_line(&self, con: &AppContext) -> std::io::Result<TreeLine> {
        let has_error = self.has_error;
        let line_type = TreeLineType::new(&self.path, &self.file_type);
        let unlisted = if let Some(children) = &self.children {
            // number of not listed children
            children.len() - self.next_child_idx
        } else {
            0
        };
        let metadata = fs::symlink_metadata(&self.path)?;
        let subpath = TreeLine::make_displayable_name(&self.subpath, &self.path, &line_type, con);
        let name = TreeLine::make_displayable_name(&self.name, &self.path, &line_type, con);

        Ok(TreeLine {
            left_branchs: vec![false; self.depth as usize].into_boxed_slice(),
            depth: self.depth,
            name,
            subpath,
            path: self.path.clone(),
            line_type,
            has_error,
            nb_kept_children: self.nb_kept_children as usize,
            unlisted,
            score: self.score,
            direct_match: self.direct_match,
            sum: None,
            metadata,
            git_status: None,
        })
    }
}