diff --git a/CHANGELOG.md b/CHANGELOG.md index 68c6505..b4b2176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. +## [0.1.0] - 2023-07-03 + +### Documentation + +- Changed roadmap +- Updated roadmap + +### Features + +- Implemented CoC flag +- Quiet flag +- Made SUCCESS/FAILURE emoji const +- Added flag no-emoji + +### Refactor + +- Made code more idiomatic + ## [0.0.7] - 2023-07-02 ### Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index ab04531..6dd9650 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,7 @@ dependencies = [ [[package]] name = "gg" -version = "0.0.6" +version = "0.1.0" dependencies = [ "clap", "clap_mangen", diff --git a/Cargo.toml b/Cargo.toml index 6212798..10a1fc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gg" -version = "0.0.7" +version = "0.1.0" edition = "2021" authors = ["Christina Sørensen"] repository = "https://github.com/cafkafk/gg" diff --git a/README.org b/README.org index 101aead..1a922e0 100644 --- a/README.org +++ b/README.org @@ -9,15 +9,17 @@ those that use declarative operating systems and package managers. Although this isn't really a case where it matters *that* much for performance, being written in rust instead of e.g. /janky/ scripting languages does also mean -it is snappy and reliable, and the /extensive/ testing helps ensure regressions -aren't introduced. +it is snappy and reliable, and the /extensive/ (hardly, but eventually) testing +helps ensure regressions aren't introduced. -That said, we're in 0.0.Z, *here be dragons* for now. +That said, we're in 0.Y.Z, *here be dragons* for now (although a little less each +commit). ** Installation #+begin_src sh -$ git clone https://github.com/cafkafk/git -$ ./install +$ git clone https://github.com/cafkafk/gg +$ cd gg +$ cargo install --path . #+end_src ** Configuration diff --git a/bin/install b/bin/install index 6133cdf..c4cde3b 100755 --- a/bin/install +++ b/bin/install @@ -1,3 +1,2 @@ #!/usr/bin/env bash -cargo rustc --release -- -C target-cpu=native cargo install --path . diff --git a/doc/roadmap.org b/doc/roadmap.org index 438ad75..8ebeb22 100644 --- a/doc/roadmap.org +++ b/doc/roadmap.org @@ -1,15 +1,27 @@ #+title: Roadmap +* 0.2.0 (maybe) +- [ ] Links in categories? +* 0.1.2 +- [ ] Implement Quiet flag +* 0.1.1 +- [ ] Implement no-emoji flag +* 0.1.0 [100%] [5/5] +- [X] No functionality regressions + - [X] commit works in quick, fast + - [X] commit with edit works +- [X] Repo Flags Finished +- [X] Optional Fields +- [X] Subcommands + - [X] Quiet flag (wont rn) + - [X] Do something about coc flag +- [X] UX + - [X] Change failure emotes + - [X] Flag for disabling emotes + * Roadmap [0%] [0/4] - [ ] Custom operation sequences - [ ] Generic repositories - [ ] Version pinning - [ ] libgit2 (maybe) -* 0.1.0 [60%] [3/5] -- [X] No functionality regressions - - [X] commit works in quick, fast - - [X] commit with edit works -- [X] Repo Flags Finished - [ ] Category Flags Finished -- [X] Optional Fields -- [ ] Subcommands diff --git a/src/cli.rs b/src/cli.rs index 10e0657..29d0183 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -59,10 +59,18 @@ pub struct Args { #[arg(long)] pub warranty: bool, - /// Print code-of-conduct information (not implemented) + /// Print code-of-conduct information #[arg(long)] pub code_of_conduct: bool, + /// Try to be as quiet as possible (unix philosophy) (not imlemented) + #[arg(short, long)] + pub quiet: bool, + + /// No emoji (not imlemented) + #[arg(short, long)] + pub no_emoji: bool, + #[command(subcommand)] pub command: Option, } diff --git a/src/git.rs b/src/git.rs index 53e093c..e495b4e 100644 --- a/src/git.rs +++ b/src/git.rs @@ -25,6 +25,8 @@ use std::os::unix::fs::symlink; use std::path::Path; use std::{fs, process::Command}; +use crate::utils::strings::{FAILURE_EMOJI, SUCCESS_EMOJI}; + /// An enum containing flags that change behaviour of repos and categories #[derive(PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum RepoFlags { @@ -53,7 +55,7 @@ pub enum RepoFlags { /// For diagrams of the underlying architecture, consult ARCHITECHTURE.md /// /// -#[derive(PartialEq, Debug, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Config { /// map of all categories /// @@ -66,7 +68,7 @@ pub struct Config { /// Represents a category of repositories /// /// This allows you to organize your repositories into categories -#[derive(PartialEq, Debug, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Category { #[serde(skip_serializing_if = "Option::is_none")] pub flags: Option>, // FIXME: not implemented @@ -78,7 +80,7 @@ pub struct Category { } /// Contain fields for a single link. -#[derive(PartialEq, Debug, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct Links { /// The name of the link pub name: String, @@ -87,7 +89,7 @@ pub struct Links { } /// Holds a single git repository and related fields. -#[derive(PartialEq, Debug, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct GitRepo { pub name: String, pub path: String, @@ -101,7 +103,7 @@ pub struct GitRepo { //////////////////////////////////// /// Represents a single operation on a repository -struct SeriesItem<'series> { +pub struct SeriesItem<'series> { /// The string to be displayed to the user operation: &'series str, /// The closure representing the actual operation @@ -114,7 +116,10 @@ struct SeriesItem<'series> { fn handle_file_exists(selff: &Links, tx_path: &Path, rx_path: &Path) { match rx_path.read_link() { - Ok(file) if file.canonicalize().unwrap() == tx_path.canonicalize().unwrap() => { + Ok(file) + if file.canonicalize().expect("failed to canonicalize file") + == tx_path.canonicalize().expect("failed to canonicalize path") => + { debug!( "Linking {} -> {} failed: file already linked", &selff.tx, &selff.rx @@ -291,7 +296,7 @@ impl GitRepo { /// Removes a repository (not implemented) /// /// Kept here as a reminder that we probably shouldn't do this - fn remove(&self) -> Result<(), std::io::Error> { + fn remove() -> Result<(), std::io::Error> { // https://doc.rust-lang.org/std/fs/fn.remove_dir_all.html unimplemented!("This seems to easy to missuse/exploit"); // fs::remove_dir_all(format!("{}{}", &self.path, &self.name)) @@ -327,7 +332,7 @@ impl Config { where F: Fn(&GitRepo), { - for (_, category) in self.categories.iter() { + for category in self.categories.values() { for (_, repo) in category.repos.as_ref().expect("failed to get repos").iter() { f(repo); } @@ -342,14 +347,13 @@ impl Config { where F: Fn(&GitRepo) -> bool, { - for (_, category) in self.categories.iter() { + for category in self.categories.values() { for (_, repo) in category.repos.as_ref().expect("failed to get repos").iter() { - let mut sp = - Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into()); + let mut sp = Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op)); if f(repo) { - sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(SUCCESS_EMOJI, format!("{}: {}", repo.name, op)); } else { - sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(FAILURE_EMOJI, format!("{}: {}", repo.name, op)); } } } @@ -396,17 +400,16 @@ impl Config { /// self.series_on_all(series); /// ``` pub fn series_on_all(&self, closures: Vec) { - for (_, category) in self.categories.iter() { + for category in self.categories.values() { for (_, repo) in category.repos.as_ref().expect("failed to get repos").iter() { - for instruction in closures.iter() { + for instruction in &closures { let f = &instruction.closure; let op = instruction.operation; - let mut sp = - Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into()); + let mut sp = Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op)); if f(repo) { - sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(SUCCESS_EMOJI, format!("{}: {}", repo.name, op)); } else { - sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(FAILURE_EMOJI, format!("{}: {}", repo.name, op)); break; } } @@ -443,17 +446,16 @@ impl Config { /// self.all_on_all(series); /// ``` pub fn all_on_all(&self, closures: Vec) { - for (_, category) in self.categories.iter() { + for category in self.categories.values() { for (_, repo) in category.repos.as_ref().expect("failed to get repos").iter() { - for instruction in closures.iter() { + for instruction in &closures { let f = &instruction.closure; let op = instruction.operation; - let mut sp = - Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into()); + let mut sp = Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op)); if f(repo) { - sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(SUCCESS_EMOJI, format!("{}: {}", repo.name, op)); } else { - sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into()); + sp.stop_and_persist(FAILURE_EMOJI, format!("{}: {}", repo.name, op)); } } } @@ -465,22 +467,22 @@ impl Config { /// Tries to pull all repositories, skips if fail. pub fn pull_all(&self) { debug!("exectuting pull_all"); - self.on_all_spinner("pull", |repo| repo.pull()); + self.on_all_spinner("pull", GitRepo::pull); } /// Tries to clone all repositories, skips if fail. pub fn clone_all(&self) { debug!("exectuting clone_all"); - self.on_all_spinner("clone", |repo| repo.clone()); + self.on_all_spinner("clone", GitRepo::clone); } /// Tries to add all work in all repositories, skips if fail. pub fn add_all(&self) { debug!("exectuting clone_all"); - self.on_all_spinner("add", |repo| repo.add_all()); + self.on_all_spinner("add", GitRepo::add_all); } /// Tries to commit all repositories one at a time, skips if fail. pub fn commit_all(&self) { debug!("exectuting clone_all"); - self.on_all_spinner("commit", |repo| repo.commit()); + self.on_all_spinner("commit", GitRepo::commit); } /// Tries to commit all repositories with msg, skips if fail. pub fn commit_all_msg(&self, msg: &str) { @@ -494,11 +496,11 @@ impl Config { let series: Vec = vec![ SeriesItem { operation: "pull", - closure: Box::new(move |repo: &GitRepo| repo.pull()), + closure: Box::new(GitRepo::pull), }, SeriesItem { operation: "add", - closure: Box::new(move |repo: &GitRepo| repo.add_all()), + closure: Box::new(GitRepo::add_all), }, SeriesItem { operation: "commit", @@ -506,7 +508,7 @@ impl Config { }, SeriesItem { operation: "push", - closure: Box::new(move |repo: &GitRepo| repo.push()), + closure: Box::new(GitRepo::push), }, ]; self.all_on_all(series); @@ -518,19 +520,19 @@ impl Config { let series: Vec = vec![ SeriesItem { operation: "pull", - closure: Box::new(move |repo: &GitRepo| repo.pull()), + closure: Box::new(GitRepo::pull), }, SeriesItem { operation: "add", - closure: Box::new(move |repo: &GitRepo| repo.add_all()), + closure: Box::new(GitRepo::add_all), }, SeriesItem { operation: "commit", - closure: Box::new(move |repo: &GitRepo| repo.commit()), + closure: Box::new(move |repo: &GitRepo| repo.commit_with_msg(msg)), }, SeriesItem { operation: "push", - closure: Box::new(move |repo: &GitRepo| repo.push()), + closure: Box::new(GitRepo::push), }, ]; self.series_on_all(series); @@ -541,7 +543,7 @@ impl Config { /// Tries to link all repositories, skips if fail. pub fn link_all(&self) { debug!("exectuting link_all"); - for link in self.links.iter() { + for link in &self.links { link.link(); } } diff --git a/src/main.rs b/src/main.rs index 6012201..d97985b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,8 @@ fn main() { match &args { args if args.license => println!("{}", utils::strings::INTERACTIVE_LICENSE), args if args.warranty => println!("{}", utils::strings::INTERACTIVE_WARRANTY), - args if args.code_of_conduct => unimplemented!(), + args if args.code_of_conduct => println!("{}", utils::strings::INTERACTIVE_COC), + args if args.quiet => todo!(), _ => (), } match &mut args.command { @@ -99,7 +100,7 @@ fn main() { config.commit_all(); } Some(Commands::CommitMsg { msg }) => { - config.commit_all_msg(msg.as_ref().unwrap()); + config.commit_all_msg(msg.as_ref().expect("failed to get message from input")); } None => (), } @@ -154,8 +155,6 @@ mod config { }, ); } - // let yaml = serde_yaml::to_string(&config).unwrap(); - // println!("{}", yaml); } #[test] fn read_config_populate() { @@ -163,13 +162,13 @@ mod config { } #[test] fn write_config() { - let root = current_dir().unwrap(); + let root = current_dir().expect("failed to get current dir"); let config = Config::new( &RelativePath::new("./src/test/config.yaml") .to_logical_path(&root) .into_os_string() .into_string() - .unwrap(), + .expect("failed to turn config into string"), ); let mut test_file = File::create( @@ -177,11 +176,13 @@ mod config { .to_logical_path(&root) .into_os_string() .into_string() - .unwrap(), + .expect("failed to turn config into string"), ) .expect("failed to create test file"); - let contents = serde_yaml::to_string(&config).unwrap(); - test_file.write_all(contents.as_bytes()).unwrap(); + let contents = serde_yaml::to_string(&config).expect("failed to turn config into string"); + test_file + .write_all(contents.as_bytes()) + .expect("failed to write contents of config into file"); let test_config = Config::new(&RelativePath::new("./src/test/test.yaml").to_string()); assert_eq!(config, test_config); @@ -205,13 +206,13 @@ mod config { } #[test] fn is_config_readable() { - let root = current_dir().unwrap(); + let root = current_dir().expect("failed to get current dir"); let config = Config::new( &RelativePath::new("./src/test/config.yaml") .to_logical_path(root) .into_os_string() .into_string() - .unwrap(), + .expect("failed to turnn config into string"), ); let flags = vec![Clone, Push]; diff --git a/src/test/test.yaml b/src/test/test.yaml index b5b3327..e433730 100644 --- a/src/test/test.yaml +++ b/src/test/test.yaml @@ -15,6 +15,7 @@ categories: flags: - Clone - Push + empty: {} stuff: flags: [] repos: @@ -29,13 +30,6 @@ categories: config: flags: [] repos: - qmk_firmware: - name: qmk_firmware - path: /home/ces/org/src/git/ - url: git@github.com:cafkafk/qmk_firmware.git - flags: - - Clone - - Push starship: name: starship path: /home/ces/org/src/git/ @@ -43,7 +37,13 @@ categories: flags: - Clone - Push - empty: {} + qmk_firmware: + name: qmk_firmware + path: /home/ces/org/src/git/ + url: git@github.com:cafkafk/qmk_firmware.git + flags: + - Clone + - Push links: - name: gg rx: /home/ces/.config/gg diff --git a/src/utils/dir.rs b/src/utils/dir.rs index 1530bab..9e90f71 100644 --- a/src/utils/dir.rs +++ b/src/utils/dir.rs @@ -50,7 +50,7 @@ pub fn home_dir() -> String { /// /// WARNING: NOT THREAD SAFE fn change_dir_repo(path: &str, name: &str) { - let mut full_path: String = "".to_owned(); + let mut full_path: String = String::new(); full_path.push_str(path); full_path.push_str(name); let root = Path::new(&full_path); diff --git a/src/utils/strings.rs b/src/utils/strings.rs index 75d7bba..876b80d 100644 --- a/src/utils/strings.rs +++ b/src/utils/strings.rs @@ -46,8 +46,22 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. "; +pub const INTERACTIVE_COC: &str = "\ +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual identity +and orientation. For more, see http://contributor-covenant.org/version/1/4"; + /// Contains the message for quick commit subcommand pub const QUICK_COMMIT: &str = "git: quick commit"; /// Contains the message for fast commit subcommand pub const FAST_COMMIT: &str = "git: fast commit"; + +/// Success emoji +pub const SUCCESS_EMOJI: &str = "✔"; + +/// Failure emoji +pub const FAILURE_EMOJI: &str = "❌";