Initial git_info and colorblind implementation

main
Michal 2 years ago
parent c2b963556b
commit 59f3c69f72
No known key found for this signature in database
GPG Key ID: A6A1A4DCB22279B9

85
Cargo.lock generated

@ -18,6 +18,30 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "ansi-str"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e50acdf02a3ac61856d5c8d576a8b5fb452a6549f667ca29fefaa18c2cd05135"
dependencies = [
"ansitok",
]
[[package]]
name = "ansitok"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2c6eb31f539d8fc1df948eb26452d6c781be4c9883663e7acb258644b71d5b1"
dependencies = [
"nom",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -161,6 +185,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]] [[package]]
name = "mimalloc" name = "mimalloc"
version = "0.1.29" version = "0.1.29"
@ -170,6 +200,22 @@ dependencies = [
"libmimalloc-sys", "libmimalloc-sys",
] ]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "numtoa" name = "numtoa"
version = "0.1.0" version = "0.1.0"
@ -194,8 +240,10 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2" checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2"
dependencies = [ dependencies = [
"ansi-str",
"bytecount", "bytecount",
"fnv", "fnv",
"strip-ansi-escapes",
"unicode-width", "unicode-width",
] ]
@ -291,6 +339,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "strip-ansi-escapes"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
dependencies = [
"vte",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -314,6 +371,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97" checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97"
dependencies = [ dependencies = [
"ansi-str",
"papergrid", "papergrid",
"tabled_derive", "tabled_derive",
"unicode-width", "unicode-width",
@ -380,12 +438,39 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vte"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
dependencies = [
"arrayvec",
"utf8parse",
"vte_generate_state_changes",
]
[[package]]
name = "vte_generate_state_changes"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

@ -27,6 +27,6 @@ serde = { version = "1.0.139", default-features = false }
serde_derive = { version = "1.0.139", default-features = false } serde_derive = { version = "1.0.139", default-features = false }
libc = { version = "0.2.126", default-features = false } libc = { version = "0.2.126", default-features = false }
colored = { version = "2.0.0", default-features = false } colored = { version = "2.0.0", default-features = false }
tabled = { version = "0.8.0", default-features = false, features = ["derive"] } tabled = { version = "0.8.0", default-features = false, features = ["derive", "color"] }
termion = { version = "1.5.6", default-features = false } termion = { version = "1.5.6", default-features = false }
regex = { version = "1.6.0", default-features = false, features = ["std"] } regex = { version = "1.6.0", default-features = false, features = ["std"] }

@ -5,10 +5,24 @@ You'll never have to work(space) another day in your life!
```toml ```toml
[mode.workspace] [mode.workspace]
git_info = true
colorblind = true
``` ```
Oh, this is awkward. It seems like there *is no* workspace-specific config yet. I'm open to suggestions though! Currently, Workspace mode only has 2 options, both pertaining to the display of information. (`mlc info`)
The first key is `git_info`, which is a boolean value. If it is true, the git information will be displayed alongside repository information.
This information will be formatted as so: `D Pl Ps <Latest Commit Hash>`
The key for the values is as follows:
- D: Whether the repository is dirty or not (unstaged changes)
- Pl: Whether there are unpulled changes at the remote
- Ps: Whether there are unpushed changes in your local repository
These will be typically displayed in either Green (Clean) or Red (Dirty)
However, if colorblind is set to true, the colors will instead be set to Blue (Clean) or Dark Red (Dirty), to be more discernible to colorblind users
### For Now... ### For Now...

@ -11,8 +11,6 @@ enabled = true
key = "michal@tar.black" key = "michal@tar.black"
on_gen = true on_gen = true
[mode.workspace]
[repositories] [repositories]
repos = [ repos = [
"crs:malachite/development", "crs:malachite/development",

@ -2,16 +2,9 @@
mode = "workspace" mode = "workspace"
smart_pull = true smart_pull = true
[mode.repository]
name = ""
build_on_update = false
[mode.repository.signing]
enabled = false
key = ""
on_gen = false
[mode.workspace] [mode.workspace]
git_info = true
colorblind = true
[repositories] [repositories]
repos = [ repos = [

@ -8,4 +8,5 @@ pub enum AppExitCode {
NoPkgs = 7, NoPkgs = 7,
ConfigParseError = 8, ConfigParseError = 8,
InvalidRepo = 9, InvalidRepo = 9,
NotInit = 10,
} }

@ -24,8 +24,8 @@ pub struct ConfigBase {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct ConfigMode { pub struct ConfigMode {
pub repository: ConfigModeRepository, pub repository: Option<ConfigModeRepository>,
pub workspace: ConfigModeWorkspace, pub workspace: Option<ConfigModeWorkspace>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -43,7 +43,13 @@ pub struct ConfigModeRepositorySigning {
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct ConfigModeWorkspace {} pub struct ConfigModeWorkspace {
pub git_info: bool,
pub colorblind: bool,
/* pub backup: bool,
pub backup_dir: Option<String>, TODO: Implement backup
*/
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct ConfigRepositories { pub struct ConfigRepositories {

@ -1,4 +1,5 @@
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::too_many_lines)]
use clap::Parser; use clap::Parser;
use std::env; use std::env;

@ -8,12 +8,18 @@ pub fn build(packages: &[String], exclude: Vec<String>, no_regen: bool, verbose:
log!(verbose, "Config: {:?}", config); log!(verbose, "Config: {:?}", config);
let all = packages.is_empty(); let all = packages.is_empty();
log!(verbose, "All: {:?}", all); log!(verbose, "All: {:?}", all);
let sign = if config.mode.repository.signing.enabled && config.mode.repository.signing.on_gen { let sign = if config.mode.repository.as_ref().unwrap().signing.enabled
&& config.mode.repository.as_ref().unwrap().signing.on_gen
{
false false
} else { } else {
config.mode.repository.signing.enabled config.mode.repository.as_ref().unwrap().signing.enabled
}; };
log!(verbose, "Signing: {:?}", config.mode.repository.signing); log!(
verbose,
"Signing: {:?}",
config.mode.repository.unwrap().signing
);
// Get list of repos and subtract exclude // Get list of repos and subtract exclude
let mut repos: Vec<Repo> = config.repositories; let mut repos: Vec<Repo> = config.repositories;

@ -9,8 +9,9 @@ pub fn clean(verbose: bool) {
.map(|x| x.unwrap().path().display().to_string()) .map(|x| x.unwrap().path().display().to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
// Remove all files/dirs in the current directory, excluding ./mlc.toml // Remove all files/dirs in the current directory, excluding ./mlc.toml and .git
dirs.retain(|x| *x != "./mlc.toml"); dirs.retain(|x| *x != "./mlc.toml");
dirs.retain(|x| *x != "./.git");
log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs); log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs);
for dir in dirs { for dir in dirs {
std::fs::remove_dir_all(dir).unwrap(); std::fs::remove_dir_all(dir).unwrap();

@ -16,7 +16,9 @@ pub fn clone(verbose: bool) {
.collect::<Vec<String>>(); .collect::<Vec<String>>();
dirs.retain(|x| *x != "./mlc.toml"); dirs.retain(|x| *x != "./mlc.toml");
dirs.retain(|x| *x != "./out"); dirs.retain(|x| *x != "./out");
dirs.retain(|x| *x != format!("./{}", config.mode.repository.name)); if config.mode.repository.is_some() {
dirs.retain(|x| *x != format!("./{}", config.mode.repository.as_ref().unwrap().name));
}
log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs); log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs);
// Creates a vector of the difference between cloned repos and repos defined in config // Creates a vector of the difference between cloned repos and repos defined in config

@ -1,46 +1,168 @@
use crate::{info, log}; use crate::{crash, info, internal::AppExitCode, log};
use colored::Colorize; use colored::Colorize;
use std::env; use std::env;
use std::process::Command;
use tabled::Tabled; use tabled::Tabled;
// For displaying the table of contents // For displaying the table of contents
#[derive(Clone, tabled::Tabled, Debug)]
struct RepoDisplayGit {
#[tabled(rename = "Name")]
name: String,
#[tabled(rename = "URL")]
url: String,
#[tabled(skip)]
priority: usize,
#[tabled(rename = "Git Info")]
git_info: String,
}
#[derive(Clone, tabled::Tabled, Debug)] #[derive(Clone, tabled::Tabled, Debug)]
struct RepoDisplay { struct RepoDisplay {
#[tabled(rename = "Name")] #[tabled(rename = "Name")]
name: String, name: String,
#[tabled(rename = "URL")] #[tabled(rename = "URL")]
url: String, url: String,
#[tabled(rename = "Priority")] #[tabled(skip)]
priority: usize, priority: usize,
} }
pub fn git_status(verbose: bool, repo: &str, colorblind: bool) -> String {
let dir = env::current_dir().unwrap();
log!(
verbose,
"Current directory: {}",
env::current_dir().unwrap().display()
);
env::set_current_dir(&repo).unwrap_or_else(|e| {
crash!(
AppExitCode::NotInit,
"Failed to enter directory {} for Git info: {}",
repo,
e.to_string()
);
});
log!(verbose, "Current directory: {}", repo);
Command::new("git")
.args(&["remote", "update"])
.output()
.unwrap();
let output = Command::new("git").arg("status").output().unwrap();
let output = String::from_utf8(output.stdout).unwrap();
log!(verbose, "Git status: {}", output);
let unstaged = output.contains("Changes not staged for commit");
let untracked = output.contains("Untracked files");
let dirty = unstaged || untracked;
let pull = output.contains("Your branch is behind");
let push = output.contains("Your branch is ahead");
let latest_commit = Command::new("git")
.args(&["log", "--pretty=%h", "-1"])
.output()
.unwrap();
let mut latest_commit = String::from_utf8(latest_commit.stdout).unwrap();
latest_commit.retain(|c| !c.is_whitespace());
let output = if colorblind {
format!(
"{} {} {} {}",
if dirty { "D".red() } else { "D".bright_blue() },
if pull { "Pl".red() } else { "Pl".bright_blue() },
if push { "Ps".red() } else { "Ps".bright_blue() },
latest_commit
)
} else {
format!(
"{} {} {} {}",
if dirty { "D".red() } else { "D".green() },
if pull { "Pl".red() } else { "Pl".green() },
if push { "Ps".red() } else { "Ps".green() },
latest_commit
)
};
env::set_current_dir(&dir).unwrap();
log!(verbose, "Current directory: {}", dir.display());
output
}
pub fn info(verbose: bool) { pub fn info(verbose: bool) {
log!(verbose, "Showing Info"); log!(verbose, "Showing Info");
let config = crate::internal::parse_cfg(verbose); let config = crate::internal::parse_cfg(verbose);
log!(verbose, "Config: {:?}", config); log!(verbose, "Config: {:?}", config);
let git_info = if config.mode.workspace.is_some() {
config.mode.workspace.as_ref().unwrap().git_info
} else {
false
};
log!(verbose, "Git info: {}", git_info);
let colorblind = if config.mode.workspace.is_some() {
config.mode.workspace.as_ref().unwrap().colorblind
} else {
false
};
log!(verbose, "Colorblind: {}", colorblind);
// Add the branch to the name if it's not the default branch for said repository // Add the branch to the name if it's not the default branch for said repository
let repos_unparsed = config.repositories; let repos_unparsed = config.repositories;
let mut repos = vec![]; let mut repos = vec![];
let mut repos_git = vec![];
for repo in repos_unparsed { for repo in repos_unparsed {
// Get name with branch, '/' serving as the delimiter
let name = if repo.branch.is_some() { let name = if repo.branch.is_some() {
format!("{}/{}", repo.name, repo.branch.unwrap()) format!("{}/{}", repo.name, repo.branch.unwrap())
} else { } else {
repo.name.clone() repo.name.clone()
}; };
repos.push(RepoDisplay {
name, // Get git info, if applicable
url: repo.url, let git_info_string = if git_info {
priority: repo.priority, Some(git_status(
}); verbose,
&repo.name,
config.mode.workspace.as_ref().unwrap().colorblind,
))
} else {
None
};
// Push to the correct vector, we're using a separate vector for git info because
// the struct we're displaying is different
if git_info {
repos_git.push(RepoDisplayGit {
name,
url: repo.url.clone(),
priority: repo.priority,
git_info: git_info_string.unwrap(),
});
} else {
repos.push(RepoDisplay {
name,
url: repo.url.clone(),
priority: repo.priority,
});
}
} }
log!(verbose, "Repos: {:?}", repos); log!(verbose, "Repos: {:?}", repos);
// Sort by priority // Sort by priority
repos.sort_by(|a, b| b.priority.cmp(&a.priority)); repos.sort_by(|a, b| b.priority.cmp(&a.priority));
log!(verbose, "Repos Sorted: {:?}", repos); repos_git.sort_by(|a, b| b.priority.cmp(&a.priority));
if git_info {
log!(verbose, "Repos Sorted: {:?}", repos_git);
} else {
log!(verbose, "Repos Sorted: {:?}", repos);
}
// Displaying basic info about the Malachite Repository // Displaying basic info about the Malachite Repository
let internal_name = if config.mode.repository.name.is_empty() { let internal_name = if config.mode.repository.is_none()
|| config.mode.repository.as_ref().unwrap().name.is_empty()
{
env::current_dir() env::current_dir()
.unwrap() .unwrap()
.file_name() .file_name()
@ -49,17 +171,13 @@ pub fn info(verbose: bool) {
.unwrap() .unwrap()
.to_string() .to_string()
} else { } else {
config.mode.repository.name config.mode.repository.unwrap().name
}; };
let name = format!( let name = format!(
"{} \"{}\":", "{} \"{}\":",
if config.base.mode == "repository" { // Sidenote: It should NOT be this convoluted to capitalise the first character of a string in rust. What the fuck.
"Repository".to_string() String::from_utf8_lossy(&[config.base.mode.as_bytes()[0].to_ascii_uppercase()])
} else if config.base.mode == "workspace" { + &config.base.mode[1..],
"Workspace".to_string()
} else {
"".to_string()
},
internal_name internal_name
); );
@ -70,16 +188,42 @@ pub fn info(verbose: bool) {
}; };
// Create table for displaying info // Create table for displaying info
let table = tabled::Table::new(&repos) let table = if git_info {
.with(tabled::Style::modern()) tabled::Table::new(&repos_git)
.with(tabled::Width::wrap(width as usize)) .with(tabled::Style::modern())
.to_string(); .with(tabled::Width::wrap(width as usize))
.to_string()
} else {
tabled::Table::new(&repos)
.with(tabled::Style::modern())
.with(tabled::Width::wrap(width as usize))
.to_string()
};
// Get length of Vec for displaying in the table
let len = if git_info {
repos_git.len()
} else {
repos.len()
};
// Print all of the info // Print all of the info
info!("{}", name); info!("{}", name);
info!( info!("Local Repositories: {}", len);
"Local Repositories: {}", println!("{}", table);
repos.len().to_string().green().bold() if config.mode.workspace.is_some() && config.mode.workspace.as_ref().unwrap().git_info {
); info!(
println!("{}", table.bold()); "Key: \n \
D: Dirty - Unstaged Changes \n \
Pl: Pull - Changes at Remote \n \
Ps: Push - Unpushed Changes \n \
{}: Applies, {}: Does Not Apply",
" ".on_red(),
if config.mode.workspace.unwrap().colorblind {
" ".on_bright_blue()
} else {
" ".on_green()
}
);
}
} }

@ -10,11 +10,7 @@ struct PullParams {
no_regen: bool, no_regen: bool,
} }
fn do_the_pulling( fn do_the_pulling(repos: Vec<String>, verbose: bool, params: &PullParams) {
repos: Vec<String>,
verbose: bool,
params: &PullParams,
) {
for repo in repos { for repo in repos {
// Set root dir to return after each git pull // Set root dir to return after each git pull
let root_dir = env::current_dir().unwrap(); let root_dir = env::current_dir().unwrap();
@ -104,7 +100,11 @@ pub fn pull(packages: Vec<String>, exclude: &[String], verbose: bool, no_regen:
let smart_pull = config.base.smart_pull; let smart_pull = config.base.smart_pull;
log!(verbose, "Smart pull: {}", smart_pull); log!(verbose, "Smart pull: {}", smart_pull);
// Read build_on_update from config // Read build_on_update from config
let build_on_update = config.mode.repository.build_on_update; let build_on_update = if config.mode.repository.is_some() {
config.mode.repository.unwrap().build_on_update
} else {
false
};
log!(verbose, "Build on update: {}", build_on_update); log!(verbose, "Build on update: {}", build_on_update);
// Read repos from config // Read repos from config
@ -142,6 +142,6 @@ pub fn pull(packages: Vec<String>, exclude: &[String], verbose: bool, no_regen:
smart_pull, smart_pull,
build_on_update, build_on_update,
no_regen, no_regen,
} },
); );
} }

@ -10,7 +10,7 @@ pub fn generate(verbose: bool) {
log!(verbose, "Config: {:?}", config); log!(verbose, "Config: {:?}", config);
// Get repository name from config // Get repository name from config
let name = config.mode.repository.name; let name = &config.mode.repository.as_ref().unwrap().name;
log!(verbose, "Name: {}", name); log!(verbose, "Name: {}", name);
info!("Generating repository: {}", name); info!("Generating repository: {}", name);
@ -39,7 +39,9 @@ pub fn generate(verbose: bool) {
log!(verbose, "Current dir: {:?}", env::current_dir().unwrap()); log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
// Sign all package files in repository if signing and on_gen are true // Sign all package files in repository if signing and on_gen are true
if config.mode.repository.signing.enabled && config.mode.repository.signing.on_gen { if config.mode.repository.as_ref().unwrap().signing.enabled
&& config.mode.repository.as_ref().unwrap().signing.on_gen
{
// Get a list of all .tar.* files in repository // Get a list of all .tar.* files in repository
let files = fs::read_dir("./").unwrap(); let files = fs::read_dir("./").unwrap();
for file in files { for file in files {
@ -52,7 +54,7 @@ pub fn generate(verbose: bool) {
"-c", "-c",
&format!( &format!(
"gpg --default-key {} --detach-sign {}", "gpg --default-key {} --detach-sign {}",
config.mode.repository.signing.key, config.mode.repository.as_ref().unwrap().signing.key,
file.file_name().to_str().unwrap() file.file_name().to_str().unwrap()
), ),
]) ])

Loading…
Cancel
Save