git_igitt/
dialogs.rs

1use crate::app::App;
2use git2::Repository;
3use std::io::Error;
4use std::path::PathBuf;
5use tui::widgets::ListState;
6
7pub struct FileDialog<'a> {
8    pub title: &'a str,
9    pub location: PathBuf,
10    pub selection: Option<PathBuf>,
11    pub dirs: Vec<(String, bool)>,
12    pub error_message: Option<String>,
13    pub color: bool,
14    pub state: ListState,
15    pub previous_app: Option<App>,
16}
17
18impl<'a> FileDialog<'a> {
19    pub fn new(title: &'a str, color: bool) -> Result<FileDialog<'a>, Error> {
20        Ok(FileDialog {
21            title,
22            location: std::env::current_dir()?,
23            selection: None,
24            dirs: vec![],
25            error_message: None,
26            color,
27            state: ListState::default(),
28            previous_app: None,
29        })
30    }
31
32    pub fn fwd(&mut self, steps: usize) {
33        let i = match self.state.selected() {
34            Some(i) => std::cmp::min(i.saturating_add(steps), self.dirs.len() - 1),
35            None => 0,
36        };
37        self.state.select(Some(i));
38    }
39
40    pub fn bwd(&mut self, steps: usize) {
41        let i = match self.state.selected() {
42            Some(i) => i.saturating_sub(steps),
43            None => 0,
44        };
45        self.state.select(Some(i));
46    }
47
48    pub fn on_up(&mut self, is_shift: bool) {
49        let step = if is_shift { 10 } else { 1 };
50        self.bwd(step)
51    }
52
53    pub fn on_down(&mut self, is_shift: bool) {
54        let step = if is_shift { 10 } else { 1 };
55        self.fwd(step)
56    }
57
58    pub fn on_left(&mut self) -> Result<(), String> {
59        if let Some(par) = self.location.parent() {
60            let temp_path = self.location.clone();
61            let prev = self.location.clone();
62            let path = PathBuf::from(par);
63            self.location = path.clone();
64            match self.selection_changed(Some(prev)) {
65                Ok(_) => {}
66                Err(err) => {
67                    self.location = temp_path;
68                    self.error_message = Some(format!(
69                        "Problem entering directory {}\n{}",
70                        path.display(),
71                        err
72                    ));
73                }
74            };
75        }
76        Ok(())
77    }
78
79    pub fn on_right(&mut self) -> Result<(), String> {
80        if let Some(sel) = self.state.selected() {
81            if sel == 0 {
82                return self.on_left();
83            }
84            let temp_path = self.location.clone();
85            let file = &self.dirs[sel];
86            let mut path = PathBuf::from(&self.location);
87            path.push(&file.0);
88            self.location = path.clone();
89            match self.selection_changed(None) {
90                Ok(_) => {}
91                Err(err) => {
92                    self.location = temp_path;
93                    self.error_message = Some(format!(
94                        "Problem entering directory {}\n{}",
95                        path.display(),
96                        err
97                    ));
98                }
99            };
100        }
101        Ok(())
102    }
103
104    pub fn on_enter(&mut self) {
105        if let Some(sel) = self.state.selected() {
106            let file = &self.dirs[sel];
107            let mut path = PathBuf::from(&self.location);
108            path.push(&file.0);
109            self.selection = Some(path);
110        }
111    }
112
113    pub fn selection_changed(&mut self, prev_location: Option<PathBuf>) -> Result<(), String> {
114        self.dirs = std::fs::read_dir(&self.location)
115            .map_err(|err| err.to_string())?
116            .filter_map(|path| match path {
117                Ok(path) => {
118                    if path.path().is_dir() {
119                        let is_repo = Repository::open(path.path()).is_ok();
120                        path.path()
121                            .components()
122                            .last()
123                            .and_then(|c| c.as_os_str().to_str().map(|s| (s.to_string(), is_repo)))
124                    } else {
125                        None
126                    }
127                }
128                Err(_) => None,
129            })
130            .collect();
131        self.dirs.insert(0, ("..".to_string(), false));
132
133        if self.dirs.is_empty() {
134            self.state.select(None);
135        } else if let Some(prev) = prev_location {
136            if let Some(prev_index) = prev
137                .components()
138                .last()
139                .and_then(|comp| comp.as_os_str().to_str())
140                .and_then(|dir| self.dirs.iter().position(|d| d.0 == dir))
141            {
142                self.state.select(Some(prev_index));
143            } else {
144                self.state.select(Some(0));
145            }
146        } else {
147            self.state.select(Some(0));
148        }
149        Ok(())
150    }
151
152    pub fn set_error(&mut self, msg: String) {
153        self.error_message = Some(msg);
154    }
155    pub fn clear_error(&mut self) {
156        self.error_message = None;
157    }
158}