Skip to content

Commit

Permalink
refactor: Restructure codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
Nukesor committed Jul 1, 2024
1 parent d58e1d4 commit fa390d8
Show file tree
Hide file tree
Showing 12 changed files with 567 additions and 505 deletions.
32 changes: 32 additions & 0 deletions src/commands/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::path::PathBuf;

use anyhow::Result;
use log::error;

use crate::state::{Repository, State};

pub fn add(state: &mut State, repos: Vec<PathBuf>) -> Result<()> {
// Just print the known repositories, if no arguments have been supplied.
if repos.is_empty() {
println!("Watched repositories:");
for repo in state.repositories.iter() {
println!(" - {:?}", repo.path);
}
return Ok(());
}

for path in repos {
// Check if the directory to add actually exists
if !path.exists() || !path.is_dir() {
error!("Cannot find repository at {:?}", path);
}

// Store the absolute path.
let real_path = std::fs::canonicalize(&path)?;
if !state.has_repo_at_path(&real_path) {
println!("Added repository: {:?}", &real_path);
state.repositories.push(Repository::new(real_path));
}
}
state.save()
}
144 changes: 144 additions & 0 deletions src/commands/check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use std::env::vars;
use std::time::Instant;
use std::{collections::HashMap, time::Duration};

use anyhow::Result;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use log::debug;
use rayon::iter::{IntoParallelIterator, ParallelIterator};

use crate::display::multi_progress_bar;
use crate::git::{check_local_changes, check_unpushed_commits, get_stashed_entries};
use crate::repository_info::RepositoryState;
use crate::{display::print_status, repository_info::RepositoryInfo, state::State};

pub fn check(
state: &mut State,
show_all: bool,
parallel: bool,
threads: Option<usize>,
) -> Result<()> {
let repo_infos = state.repo_infos_by_wall_time();

// Save all environment variables for later injection into git
let mut envs = HashMap::new();
for (key, value) in vars() {
envs.insert(key, value);
}

let (multi_progress, main_bar) = multi_progress_bar(repo_infos.len() as u64)?;

let repo_infos = if parallel {
// Set the amount of threads, if specified.
if let Some(threads) = threads {
rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build_global()
.unwrap();
}

let results: Result<Vec<RepositoryInfo>> = repo_infos
.into_par_iter()
.map(|mut repo_info| {
// Handle the repository and track execution time.
let start = Instant::now();
repo_info = match check_repo(&multi_progress, repo_info, &envs) {
Ok(repo_info) => repo_info,
Err(err) => {
// Make sure the bar gets incremented even if we get an error.
main_bar.inc(1);
return Err(err);
}
};
repo_info.check_time = Some(start.elapsed().as_millis() as usize);

main_bar.inc(1);
Ok(repo_info)
})
.collect();

main_bar.finish_with_message("All done: ");

results?
} else {
let mut results = Vec::new();
for repo_info in repo_infos.into_iter() {
// Handle the repository and track execution time.
let start = Instant::now();
let mut repo_info = check_repo(&multi_progress, repo_info, &envs)?;
repo_info.check_time = Some(start.elapsed().as_millis() as usize);

debug!("Check took {}ms", start.elapsed().as_millis());
results.push(repo_info);
}

results
};

// Finish and clean up the progress bar
main_bar.finish();
let _ = multi_progress.clear();

state.update_check_times(&repo_infos)?;

print_status(repo_infos, show_all)?;

Ok(())
}

/// This is a simple wrapper around the actual repo check function
/// for easier progress bar handling.
pub fn check_repo(
multi_progress: &MultiProgress,
repo_info: RepositoryInfo,
envs: &HashMap<String, String>,
) -> Result<RepositoryInfo> {
let mut bar = ProgressBar::new(3);
let spinner_style =
ProgressStyle::with_template("{duration} {spinner} {prefix:.bold.white.dim} - {wide_msg}")
.unwrap()
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ");
bar.set_style(spinner_style);

// Add the bar to the end of the multi_bar.
bar = multi_progress.add(bar);

// Enable a steady tick after adding it to the bar, to ensure correct position rendering.
bar.enable_steady_tick(Duration::from_millis(125));

// Run the actual repo handling logic.
let result = check_repo_inner(&bar, repo_info, envs);

// Clean up this repo's progress bar.
bar.disable_steady_tick();
bar.finish();
multi_progress.remove(&bar);

result
}

pub fn check_repo_inner(
bar: &ProgressBar,
mut repo_info: RepositoryInfo,
envs: &HashMap<String, String>,
) -> Result<RepositoryInfo> {
let name = repo_info.name.clone();

// Default to a `Ok` repo state.
// If anything is not ok, it'll be set in the respective function.
repo_info.state = RepositoryState::Ok;

bar.set_prefix(format!("[1/3] - {name}"));
bar.set_message(format!("{name}: Checking stash"));
get_stashed_entries(&mut repo_info, envs)?;

bar.set_prefix(format!("[2/3] - {name}"));
bar.set_message(format!("{name}: Check for local changes"));
check_local_changes(&mut repo_info, envs)?;

bar.set_prefix(format!("[3/3] - {name}"));
bar.set_message(format!("{name}: Check for unpushed commits"));
check_unpushed_commits(&mut repo_info, envs)?;

Ok(repo_info)
}
47 changes: 47 additions & 0 deletions src/commands/ignore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::path::PathBuf;

use anyhow::Result;
use log::error;

use super::unwatch;
use crate::state::{discover, State};

/// Explicitly ignore a given directory.
/// No repositories will be updated or discovered, even if it's inside a watched directory.
pub fn ignore(state: &mut State, directories: &[PathBuf]) -> Result<()> {
// First, unwatch in case any of them have been actively watched.
// This also explicitly removes any repositories that were tracked.
unwatch(state, directories)?;

for path in directories.iter() {
// Check if the directory to add actually exists
if !path.exists() || !path.is_dir() {
error!("Cannot find directory at {:?}", path);
continue;
}

// Get the absolute path
let real_path = std::fs::canonicalize(path)?;

if state.ignored.contains(&real_path) {
error!("The folder is already ignored: {:?}", &real_path);
continue;
}

// Scan the watched path for repositories, so we can forget about them
let mut repos = Vec::new();
discover(&state.ignored, &real_path, 0, &mut repos);

for repo_to_remove in repos {
println!("Forgetting about repository: {:?}", repo_to_remove.path);
state
.repositories
.retain(|repo| repo.path != repo_to_remove.path);
}

println!("Ignoring directory: {:?}", real_path);
state.ignored.push(real_path.to_owned())
}

state.save()
}
15 changes: 15 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mod add;
mod check;
mod ignore;
mod remove;
mod unwatch;
mod update;
mod watch;

pub use add::*;
pub use check::*;
pub use ignore::*;
pub use remove::*;
pub use unwatch::*;
pub use update::*;
pub use watch::*;
29 changes: 29 additions & 0 deletions src/commands/remove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::path::PathBuf;

use anyhow::Result;
use log::error;

use crate::state::State;

pub fn remove(state: &mut State, repos: Vec<PathBuf>) -> Result<()> {
for path in repos {
// Check if the directory to add actually exists
if !path.exists() || !path.is_dir() {
error!("Cannot find repository at {:?}", path);
continue;
}

// Store the absolute path.
let real_path = std::fs::canonicalize(&path)?;
if !state.has_repo_at_path(&real_path) {
error!(
"The repository at {:?} hasn't been added to geil yet.",
path
);
} else {
println!("Forgetting about repository: {:?}", &real_path);
state.repositories.retain(|repo| repo.path != real_path);
}
}
state.save()
}
38 changes: 38 additions & 0 deletions src/commands/unwatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::path::PathBuf;

use anyhow::Result;
use log::error;

use crate::state::{discover, State};

pub fn unwatch(state: &mut State, directories: &[PathBuf]) -> Result<()> {
for path in directories {
// Check if the directory to add actually exists
if !path.exists() || !path.is_dir() {
error!("Cannot find directory at {:?}", path);
continue;
}

// Get the absolute path
let real_path = std::fs::canonicalize(path)?;
if !state.watched.contains(&real_path) {
error!("The folder hasn't been watched: {:?}", &real_path);
} else {
println!("Unwatching path : {:?}", &real_path);
state.watched.retain(|path| path != &real_path);

// Scan the watched path for repositories, so we can forget about them
let mut repos = Vec::new();
discover(&state.ignored, &real_path, 0, &mut repos);

for repo_to_remove in repos {
println!("Forgetting about repository: {:?}", repo_to_remove.path);
state
.repositories
.retain(|repo| repo.path != repo_to_remove.path);
}
}
}

state.save()
}
Loading

0 comments on commit fa390d8

Please sign in to comment.