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",
]
[[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]]
name = "atty"
version = "0.2.14"
@ -161,6 +185,12 @@ dependencies = [
"cc",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mimalloc"
version = "0.1.29"
@ -170,6 +200,22 @@ dependencies = [
"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]]
name = "numtoa"
version = "0.1.0"
@ -194,8 +240,10 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453cf71f2a37af495a1a124bf30d4d7469cfbea58e9f2479be9d222396a518a2"
dependencies = [
"ansi-str",
"bytecount",
"fnv",
"strip-ansi-escapes",
"unicode-width",
]
@ -291,6 +339,15 @@ dependencies = [
"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]]
name = "strsim"
version = "0.10.0"
@ -314,6 +371,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5b2f8c37d26d87d2252187b0a45ea3cbf42baca10377c7e7eaaa2800fa9bf97"
dependencies = [
"ansi-str",
"papergrid",
"tabled_derive",
"unicode-width",
@ -380,12 +438,39 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winapi"
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 }
libc = { version = "0.2.126", 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 }
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
[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...

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

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

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

@ -24,8 +24,8 @@ pub struct ConfigBase {
#[derive(Debug, Deserialize)]
pub struct ConfigMode {
pub repository: ConfigModeRepository,
pub workspace: ConfigModeWorkspace,
pub repository: Option<ConfigModeRepository>,
pub workspace: Option<ConfigModeWorkspace>,
}
#[derive(Debug, Deserialize)]
@ -43,7 +43,13 @@ pub struct ConfigModeRepositorySigning {
}
#[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)]
pub struct ConfigRepositories {

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

@ -8,12 +8,18 @@ pub fn build(packages: &[String], exclude: Vec<String>, no_regen: bool, verbose:
log!(verbose, "Config: {:?}", config);
let all = packages.is_empty();
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
} 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
let mut repos: Vec<Repo> = config.repositories;

@ -9,8 +9,9 @@ pub fn clean(verbose: bool) {
.map(|x| x.unwrap().path().display().to_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 != "./.git");
log!(verbose, "Paths with mlc.toml excluded: {:?}", dirs);
for dir in dirs {
std::fs::remove_dir_all(dir).unwrap();

@ -16,7 +16,9 @@ pub fn clone(verbose: bool) {
.collect::<Vec<String>>();
dirs.retain(|x| *x != "./mlc.toml");
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);
// 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 std::env;
use std::process::Command;
use tabled::Tabled;
// 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)]
struct RepoDisplay {
#[tabled(rename = "Name")]
name: String,
#[tabled(rename = "URL")]
url: String,
#[tabled(rename = "Priority")]
#[tabled(skip)]
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) {
log!(verbose, "Showing Info");
let config = crate::internal::parse_cfg(verbose);
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
let repos_unparsed = config.repositories;
let mut repos = vec![];
let mut repos_git = vec![];
for repo in repos_unparsed {
// Get name with branch, '/' serving as the delimiter
let name = if repo.branch.is_some() {
format!("{}/{}", repo.name, repo.branch.unwrap())
} else {
repo.name.clone()
};
// Get git info, if applicable
let git_info_string = if git_info {
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,
url: repo.url.clone(),
priority: repo.priority,
});
}
}
log!(verbose, "Repos: {:?}", repos);
// Sort by priority
repos.sort_by(|a, b| b.priority.cmp(&a.priority));
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
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()
.unwrap()
.file_name()
@ -49,17 +171,13 @@ pub fn info(verbose: bool) {
.unwrap()
.to_string()
} else {
config.mode.repository.name
config.mode.repository.unwrap().name
};
let name = format!(
"{} \"{}\":",
if config.base.mode == "repository" {
"Repository".to_string()
} else if config.base.mode == "workspace" {
"Workspace".to_string()
} else {
"".to_string()
},
// Sidenote: It should NOT be this convoluted to capitalise the first character of a string in rust. What the fuck.
String::from_utf8_lossy(&[config.base.mode.as_bytes()[0].to_ascii_uppercase()])
+ &config.base.mode[1..],
internal_name
);
@ -70,16 +188,42 @@ pub fn info(verbose: bool) {
};
// Create table for displaying info
let table = tabled::Table::new(&repos)
let table = if git_info {
tabled::Table::new(&repos_git)
.with(tabled::Style::modern())
.with(tabled::Width::wrap(width as usize))
.to_string();
.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
info!("{}", name);
info!("Local Repositories: {}", len);
println!("{}", table);
if config.mode.workspace.is_some() && config.mode.workspace.as_ref().unwrap().git_info {
info!(
"Local Repositories: {}",
repos.len().to_string().green().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()
}
);
println!("{}", table.bold());
}
}

@ -10,11 +10,7 @@ struct PullParams {
no_regen: bool,
}
fn do_the_pulling(
repos: Vec<String>,
verbose: bool,
params: &PullParams,
) {
fn do_the_pulling(repos: Vec<String>, verbose: bool, params: &PullParams) {
for repo in repos {
// Set root dir to return after each git pull
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;
log!(verbose, "Smart pull: {}", smart_pull);
// 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);
// Read repos from config
@ -142,6 +142,6 @@ pub fn pull(packages: Vec<String>, exclude: &[String], verbose: bool, no_regen:
smart_pull,
build_on_update,
no_regen,
}
},
);
}

@ -10,7 +10,7 @@ pub fn generate(verbose: bool) {
log!(verbose, "Config: {:?}", 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);
info!("Generating repository: {}", name);
@ -39,7 +39,9 @@ pub fn generate(verbose: bool) {
log!(verbose, "Current dir: {:?}", env::current_dir().unwrap());
// 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
let files = fs::read_dir("./").unwrap();
for file in files {
@ -52,7 +54,7 @@ pub fn generate(verbose: bool) {
"-c",
&format!(
"gpg --default-key {} --detach-sign {}",
config.mode.repository.signing.key,
config.mode.repository.as_ref().unwrap().signing.key,
file.file_name().to_str().unwrap()
),
])

Loading…
Cancel
Save