use super::list_view::{ListCursor, ListFilter, Viewport};
use rootle_provider::RepoInfo;
use std::path::PathBuf;
mod keys;
mod render;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Screen {
Repos,
Destination,
Summary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Focus {
List,
Buttons,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Button {
Back,
Next,
}
pub struct CloneWizard {
screen: Screen,
repos: Vec<(RepoInfo, bool)>,
selection: ListCursor,
focus: Focus,
button: Button,
dest: PathBuf,
dest_entries: Vec<String>,
destination_selection: ListCursor,
viewport: Viewport,
filter: ListFilter,
}
impl CloneWizard {
pub fn effective_mode(&self) -> crate::mode::Mode {
if self.filter.active() {
crate::mode::Mode::Search
} else {
crate::mode::Mode::Browse
}
}
pub fn new(repos: Vec<RepoInfo>, start: PathBuf) -> Self {
let mut repos = repos;
repos.sort_by(|a, b| {
b.pushed_at
.cmp(&a.pushed_at)
.then_with(|| a.name.cmp(&b.name))
});
let repos = repos.into_iter().map(|r| (r, true)).collect();
let mut wizard = CloneWizard {
screen: Screen::Repos,
repos,
selection: ListCursor::new(),
focus: Focus::List,
button: Button::Next,
dest: start,
dest_entries: vec![],
destination_selection: ListCursor::new(),
viewport: Viewport::default(),
filter: ListFilter::default(),
};
wizard.refresh_dest();
wizard
}
fn refresh_dest(&mut self) {
let mut entries = vec!["..".to_string()];
if let Ok(read) = std::fs::read_dir(&self.dest) {
let mut dirs: Vec<String> = read
.filter_map(|e| e.ok())
.filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false))
.filter_map(|e| e.file_name().into_string().ok())
.filter(|name| !name.starts_with('.')) .collect();
dirs.sort();
entries.extend(dirs);
}
self.dest_entries = entries;
self.destination_selection.reset();
self.viewport.reset();
self.filter.clear();
}
fn checked(&self) -> impl Iterator<Item = &RepoInfo> {
self.repos.iter().filter(|(_, on)| *on).map(|(r, _)| r)
}
fn visible_repos(&self) -> Vec<usize> {
self.filter.visible(&self.repos, |(repository, _), filter| {
filter.matches(&repository.name)
})
}
fn visible_dest(&self) -> Vec<usize> {
self.filter
.visible(&self.dest_entries, |entry, filter| filter.matches(entry))
}
fn target(&self, repo: &str) -> std::path::PathBuf {
self.dest.join(repo)
}
fn buttons(&self) -> (&'static str, &'static str) {
match self.screen {
Screen::Repos | Screen::Destination => ("← Back", "Next →"),
Screen::Summary => ("← Back", "⏎ Clone"),
}
}
}