chore: merge 0.0.6 #6 from cafkafk/dev
Of major notice is: - Categories - Repo flags - Architectural overhaul - Increased documentation - Improved UX - Changed config.yaml location - Increased scope of push field - Remove potentially destructive operation - Fixed mini-license typos - Fixed testing with hashmap arch - Spinner on all repoactions - Fixed commit in quick - [**breaking**] Fixed quick, fast messages - Fixed commit with editor regression - Architectural Overview - Moved charts to doc/img - Update image locations - Moved ARCHITECTURE.md to doc/ - Added some documentation - Added roadmap - Started flakification - Added nix flake #5 - [**breaking**] Add push field - [**breaking**] Add repo flags - [**breaking**] Implemented naive categories - Started work on using spinners - Added pull flag - React to exit code of git - Started adding multi instruction logic - Added fast subcommand - Version bump to v0.0.3 - Moved install scripts to ./bin - Fixed various clippy errors - Removed unused code from flake - Improved GitRepo assoc. function debug - Removed redundant line in Cargo.toml - Created on_all for config struct - Naive nested hashmap - Generic refactor - Removed atty dependency - Removed unused ./test dir - Mvp flake working <!-- generated by git-cliff --> Signed-off-by: Christina Sørensen <christina@cafkafk.com>
This commit is contained in:
commit
8476b343ec
18 changed files with 713 additions and 159 deletions
69
Cargo.lock
generated
69
Cargo.lock
generated
|
@ -118,7 +118,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -179,7 +179,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gg"
|
||||
version = "0.0.3"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_mangen",
|
||||
|
@ -188,6 +188,7 @@ dependencies = [
|
|||
"relative-path",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"spinners",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -253,6 +254,12 @@ version = "1.0.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
|
@ -274,6 +281,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -357,6 +370,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
|
@ -380,7 +399,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -396,12 +415,56 @@ dependencies = [
|
|||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spinners"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08615eea740067d9899969bc2891c68a19c315cb1f66640af9a9ecb91b13bcab"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"maplit",
|
||||
"strum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gg"
|
||||
version = "0.0.3"
|
||||
version = "0.0.6"
|
||||
edition = "2021"
|
||||
authors = ["Christina Sørensen <christina@cafkafk.com>"]
|
||||
repository = "https://github.com/cafkafk/gg"
|
||||
|
@ -15,6 +15,7 @@ clap = { version = "4.0.22", features = ["derive"] }
|
|||
log = "0.4"
|
||||
pretty_env_logger = "0.5.0"
|
||||
relative-path = "1.8.0"
|
||||
spinners = "4.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version = "4.3.2", features = ["derive", "cargo", "env", "help"] }
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
cargo rustc --release -- -C target-cpu=native
|
||||
cp ./target/release/gg ~/.local/bin
|
||||
cargo install --path .
|
3
bin/install_debug
Executable file
3
bin/install_debug
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
# cargo rustc
|
||||
cargo install --debug --path .
|
103
doc/ARCHITECTURE.org
Normal file
103
doc/ARCHITECTURE.org
Normal file
|
@ -0,0 +1,103 @@
|
|||
#+title: Architecture
|
||||
|
||||
** Architecture
|
||||
*** Config datastructure
|
||||
|
||||
There were 3 major designs considered so far (here in chronological order).
|
||||
|
||||
**** Vec Based
|
||||
Code sketch in https://github.com/cafkafk/gg/commit/3d3b6d6646bda84333018cd621cd8bd6348b9cef
|
||||
|
||||
#+begin_src mermaid :file ./doc/img/config-struct-vec.png :width 4000px
|
||||
flowchart LR
|
||||
Co[config]
|
||||
Ca["categories (vec<category>)"]
|
||||
L[links]
|
||||
Co ==> Ca & L
|
||||
Ca ----> c1(category 1) & c2(category 2) & c3(category 3)
|
||||
subgraph Categories Vec
|
||||
c1(category 1) ==> flags1 & repos1("repos (vec<GitRepo>)")
|
||||
c2(category 2) ==> flags2 & repos2("repos (vec<GitRepo>)")
|
||||
c3(category 3) ==> flags3 & repos3("repos (vec<GitRepo>)")
|
||||
direction TB
|
||||
subgraph GitRepos
|
||||
repos1 --> gr1 & gr2 & gr3
|
||||
repos2 --> gr4 & gr5 & gr6
|
||||
repos3 --> gr7 & gr8 & gr9
|
||||
end
|
||||
direction TB
|
||||
subgraph Flags Enum
|
||||
flags1 & flags2 & flags3 -. any of .- Push & Clone
|
||||
end
|
||||
end
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
[[file:./doc/img/config-struct-vec.png]]
|
||||
|
||||
**** BTreeMap Based (nested)
|
||||
|
||||
#+begin_src mermaid :file ./doc/img/config-struct-nested.png :width 4000px
|
||||
flowchart LR
|
||||
Co[config]
|
||||
Ca["categories (BTreeMap)"]
|
||||
L[links]
|
||||
Co ==> Ca & L
|
||||
Ca -- "unique_name/key" --> c1(category 1) & c2(category 2) & c3(category 3)
|
||||
subgraph Categories BTreeMap
|
||||
c1(category 1) ==> flags1 & repos1("repos (BTreeMap)")
|
||||
c2(category 2) ==> flags2 & repos2("repos (BTreeMap)")
|
||||
c3(category 3) ==> flags3 & repos3("repos (BTreeMap)")
|
||||
direction TB
|
||||
subgraph GitRepos
|
||||
repos1 -- "unique_name/key" --> gr1 & gr2 & gr3
|
||||
repos2 -- "unique_name/key" --> gr4 & gr5 & gr6
|
||||
repos3 -- "unique_name/key" --> gr7 & gr8 & gr9
|
||||
end
|
||||
direction TB
|
||||
subgraph Flags Enum
|
||||
flags1 & flags2 & flags3 -. any of .- Push & Clone
|
||||
end
|
||||
end
|
||||
#+end_src
|
||||
|
||||
|
||||
#+RESULTS:
|
||||
[[file:./doc/img/config-struct-nested.png]]
|
||||
|
||||
**** BTreeMap Based (Store)
|
||||
|
||||
#+begin_src mermaid :file ./doc/img/config-struct-store.png :width 4000px
|
||||
flowchart LR
|
||||
S[(Store)]
|
||||
subgraph Repo Store BMapTree
|
||||
S -- "unique_name/key" ----> gr1 & gr2 & gr3
|
||||
S -- "unique_name/key" ----> gr4 & gr5 & gr6
|
||||
S -- "unique_name/key" ----> gr7 & gr8 & gr9
|
||||
end
|
||||
Co[config]
|
||||
Ca["categories (BTreeMap)"]
|
||||
L[links]
|
||||
Co ==> Ca & L
|
||||
Ca -- "unique_name/key" --> c1(category 1) & c2(category 2) & c3(category 3)
|
||||
subgraph Categories BTreeMap
|
||||
c1(category 1) ==> flags1 & repos1("repos (vec<keys>)")
|
||||
c2(category 2) ==> flags2 & repos2("repos (vec<keys>)")
|
||||
c3(category 3) ==> flags3 & repos3("repos (vec<keys>)")
|
||||
direction TB
|
||||
subgraph GitRepos
|
||||
repos1 <-. "unique_name/key" .-> gr1 & gr2 & gr3 & gr4 & gr5 & gr6 & gr7 & gr8 & gr9
|
||||
repos2 <-. "unique_name/key" .-> gr1 & gr2 & gr3 & gr4 & gr5 & gr6 & gr7 & gr8 & gr9
|
||||
repos3 <-. "unique_name/key" .-> gr1 & gr2 & gr3 & gr4 & gr5 & gr6 & gr7 & gr8 & gr9
|
||||
end
|
||||
direction TB
|
||||
subgraph Flags Enum
|
||||
flags1 & flags2 & flags3 -. any of .- Push & Clone
|
||||
end
|
||||
end
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
[[file:./doc/img/config-struct-store.png]]
|
||||
|
||||
**** Discussion
|
BIN
doc/img/config-struct-nested.png
Normal file
BIN
doc/img/config-struct-nested.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 KiB |
BIN
doc/img/config-struct-store.png
Normal file
BIN
doc/img/config-struct-store.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 436 KiB |
BIN
doc/img/config-struct-vec.png
Normal file
BIN
doc/img/config-struct-vec.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
15
doc/roadmap.org
Normal file
15
doc/roadmap.org
Normal file
|
@ -0,0 +1,15 @@
|
|||
#+title: Roadmap
|
||||
|
||||
* Roadmap [0%] [0/4]
|
||||
- [ ] Custom operation sequences
|
||||
- [ ] Generic repositories
|
||||
- [ ] Version pinning
|
||||
- [ ] libgit2 (maybe)
|
||||
* 0.1.0 [20%] [1/5]
|
||||
- [X] No functionality regressions
|
||||
- [X] commit works in quick, fast
|
||||
- [X] commit with edit works
|
||||
- [ ] Repo Flags Finished
|
||||
- [ ] Category Flags Finished
|
||||
- [ ] Optional Fields
|
||||
- [ ] Subcommands
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
cargo rustc
|
||||
cp ./target/debug/gg ~/.local/bin
|
|
@ -13,6 +13,8 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! Handles command line input
|
||||
|
||||
use crate::utils::dir::home_dir;
|
||||
use crate::utils::strings::INTERACTIVE_NOTICE;
|
||||
|
@ -75,6 +77,10 @@ pub enum Commands {
|
|||
#[command(visible_alias = "q")]
|
||||
Quick { msg: Option<String> },
|
||||
|
||||
/// Do fast pull-commit-push with msg for commit, skipping repo on failure
|
||||
#[command(visible_alias = "f")]
|
||||
Fast { msg: Option<String> },
|
||||
|
||||
/// Clone all repositories
|
||||
#[command(visible_alias = "c")]
|
||||
Clone { msg: Option<String> },
|
||||
|
|
372
src/git.rs
372
src/git.rs
|
@ -13,24 +13,60 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! Git repositories
|
||||
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spinners::{Spinner, Spinners};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::canonicalize;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::Path;
|
||||
use std::{fs, process::Command};
|
||||
|
||||
/// An enum containing flags that change behaviour of repos and categories
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub enum RepoFlags {
|
||||
/// If push is set, the repository should respond to the push subcommand
|
||||
Push,
|
||||
/// If clone is set, the repository should respond to the clone subcommand
|
||||
Clone,
|
||||
/// If pull is set, the repository should respond to the pull subcommand
|
||||
Pull,
|
||||
}
|
||||
|
||||
/// Represents the config.toml file.
|
||||
///
|
||||
/// For diagrams of the underlying architecture, consult ARCHITECHTURE.md
|
||||
///
|
||||
///
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub repos: Vec<GitRepo>,
|
||||
/// map of all categories
|
||||
///
|
||||
/// Key should conceptually be seen as the name of the category.
|
||||
pub categories: HashMap<String, Category>,
|
||||
/// A vector containing links
|
||||
pub links: Vec<Links>,
|
||||
}
|
||||
|
||||
/// Represents a category of repositories
|
||||
///
|
||||
/// This allows you to organize your repositories into categories
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Category {
|
||||
pub flags: Vec<RepoFlags>, // FIXME: not implemented
|
||||
/// map of all categories
|
||||
///
|
||||
/// Key should conceptually be seen as the name of the category.
|
||||
pub repos: HashMap<String, GitRepo>,
|
||||
}
|
||||
|
||||
/// Contain fields for a single link.
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Links {
|
||||
/// The name of the link
|
||||
pub name: String,
|
||||
pub rx: String,
|
||||
pub tx: String,
|
||||
|
@ -42,9 +78,25 @@ pub struct GitRepo {
|
|||
pub name: String,
|
||||
pub path: String,
|
||||
pub url: String,
|
||||
pub clone: bool,
|
||||
pub flags: Vec<RepoFlags>,
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
|
||||
/// Represents a single operation on a repository
|
||||
struct SeriesItem<'series> {
|
||||
/// The string to be displayed to the user
|
||||
operation: &'series str,
|
||||
/// The closure representing the actual operation
|
||||
closure: Box<dyn Fn(&GitRepo) -> (bool)>,
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
////////////////////////////////////
|
||||
|
||||
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() => {
|
||||
|
@ -66,8 +118,8 @@ fn handle_file_exists(selff: &Links, tx_path: &Path, rx_path: &Path) {
|
|||
}
|
||||
|
||||
impl Links {
|
||||
/// Creates a link from a file
|
||||
fn link(&self) {
|
||||
/// Creates the link from the link struct
|
||||
pub fn link(&self) {
|
||||
let tx_path: &Path = std::path::Path::new(&self.tx);
|
||||
let rx_path: &Path = std::path::Path::new(&self.rx);
|
||||
match rx_path.try_exists() {
|
||||
|
@ -90,81 +142,115 @@ impl Links {
|
|||
|
||||
impl GitRepo {
|
||||
/// Clones the repository to its specified folder.
|
||||
fn clone(&self) {
|
||||
if self.clone {
|
||||
fn clone(&self) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Clone) {
|
||||
// TODO: check if &self.name already exists in dir
|
||||
let out = Command::new("git")
|
||||
let output = Command::new("git")
|
||||
.current_dir(&self.path)
|
||||
.arg("clone")
|
||||
.arg(&self.url)
|
||||
.arg(&self.name)
|
||||
.status()
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to clone: {:?}", &self,));
|
||||
info!("{out}");
|
||||
output.status.success()
|
||||
} else {
|
||||
info!("{} has clone set to false, not cloned", &self.name);
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Pulls the repository if able.
|
||||
fn pull(&self) {
|
||||
let out = Command::new("git")
|
||||
fn pull(&self) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Pull) {
|
||||
let output = Command::new("git")
|
||||
.current_dir(format!("{}{}", &self.path, &self.name))
|
||||
.arg("pull")
|
||||
.status()
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to pull: {:?}", &self,));
|
||||
info!("{out}");
|
||||
output.status.success()
|
||||
} else {
|
||||
info!("{} has clone set to false, not pulled", &self.name);
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Adds all files in the repository.
|
||||
fn add_all(&self) {
|
||||
let out = Command::new("git")
|
||||
fn add_all(&self) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Push) {
|
||||
let output = Command::new("git")
|
||||
.current_dir(format!("{}{}", &self.path, &self.name))
|
||||
.arg("add")
|
||||
.arg(".")
|
||||
.status()
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to add: {:?}", &self,));
|
||||
info!("{out}");
|
||||
output.status.success()
|
||||
} else {
|
||||
info!("{} has clone set to false, not cloned", &self.name);
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Tries to commit changes in the repository.
|
||||
///
|
||||
/// # Development
|
||||
///
|
||||
/// - FIXME: this prints extra information to terminal this is because we
|
||||
/// use status() instead of output(), as that makes using the native editor
|
||||
/// easy
|
||||
#[allow(dead_code)]
|
||||
fn commit(&self) {
|
||||
let out = Command::new("git")
|
||||
fn commit(&self) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Push) {
|
||||
let status = Command::new("git")
|
||||
.current_dir(format!("{}{}", &self.path, &self.name))
|
||||
.arg("commit")
|
||||
.status()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to commit: {:?}", &self,));
|
||||
info!("{out}");
|
||||
status.success()
|
||||
} else {
|
||||
info!("{} has push set to false, not cloned", &self.name);
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Tries to commit changes with a message argument.
|
||||
fn commit_with_msg(&self, msg: &String) {
|
||||
let out = Command::new("git")
|
||||
fn commit_with_msg(&self, msg: &str) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Push) {
|
||||
let output = Command::new("git")
|
||||
.current_dir(format!("{}{}", &self.path, &self.name))
|
||||
.arg("commit")
|
||||
.arg("-m")
|
||||
.arg(msg)
|
||||
.status()
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to commit: {:?}", &self,));
|
||||
info!("{out}");
|
||||
output.status.success()
|
||||
} else {
|
||||
info!("{} has clone set to false, not cloned", &self.name);
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Attempts to push the repository.
|
||||
fn push(&self) {
|
||||
let out = Command::new("git")
|
||||
fn push(&self) -> bool {
|
||||
if self.flags.contains(&RepoFlags::Push) {
|
||||
let output = Command::new("git")
|
||||
.current_dir(format!("{}{}", &self.path, &self.name))
|
||||
.arg("push")
|
||||
.status()
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("git repo failed to push: {:?}", &self,));
|
||||
info!("{out}");
|
||||
output.status.success()
|
||||
} else {
|
||||
info!("{} has clone set to false, not cloned", &self.name);
|
||||
false
|
||||
}
|
||||
/// Removes repository
|
||||
}
|
||||
/// Removes a repository (not implemented)
|
||||
///
|
||||
/// Kept here as a reminder that we probably shouldn't do this
|
||||
fn remove(&self) -> 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))
|
||||
// fs::remove_dir_all(format!("{}{}", &self.path, &self.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/* GIT RELATED */
|
||||
/// Reads the configuration toml from a path.
|
||||
/// Loads the configuration toml from a path in to the Config struct.
|
||||
pub fn new(path: &String) -> Self {
|
||||
debug!("initializing new Config struct");
|
||||
let yaml = fs::read_to_string(path).unwrap_or_else(|_| {
|
||||
|
@ -178,54 +264,230 @@ impl Config {
|
|||
)
|
||||
})
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Runs associated function on all repos in config
|
||||
///
|
||||
/// TODO: need to be made over a generic repo type
|
||||
///
|
||||
/// NOTE: currently unused
|
||||
///
|
||||
fn on_all<F>(&self, f: F)
|
||||
where
|
||||
F: Fn(&GitRepo),
|
||||
{
|
||||
for (_, category) in self.categories.iter() {
|
||||
for (_, repo) in category.repos.iter() {
|
||||
f(repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Runs associated function on all repos in config
|
||||
///
|
||||
/// TODO: need to be made over a generic repo type
|
||||
///
|
||||
///
|
||||
fn on_all_spinner<F>(&self, op: &str, f: F)
|
||||
where
|
||||
F: Fn(&GitRepo) -> bool,
|
||||
{
|
||||
for (_, category) in self.categories.iter() {
|
||||
for (_, repo) in category.repos.iter() {
|
||||
let mut sp =
|
||||
Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into());
|
||||
if f(repo) {
|
||||
sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into());
|
||||
} else {
|
||||
sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Runs associated function on all repos in config
|
||||
///
|
||||
/// TODO: need to be made over a generic repo type
|
||||
///
|
||||
/// # Current Problem
|
||||
///
|
||||
/// The goal of this function is that it should run some function on all
|
||||
/// repos but stop executing further functions on any repo that fails,
|
||||
/// without blocking the repos that don't have an issue.
|
||||
///
|
||||
/// This is actually somewhat hairy to do, at least at 6:16 am :S
|
||||
///
|
||||
/// However, at 6:24, we're so ready! Let's go!
|
||||
///
|
||||
/// Fun fact: only the last element of a tuple must have a dynamically typed size
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// Here is an example of how an associated method could use this function.
|
||||
///
|
||||
/// ```
|
||||
/// let series: Vec<SeriesItem> = vec![
|
||||
/// SeriesItem {
|
||||
/// operation: "pull",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.pull()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "add",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.add_all()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "commit",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.commit()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "push",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.push()),
|
||||
/// },
|
||||
/// ];
|
||||
/// self.series_on_all(series);
|
||||
/// ```
|
||||
pub fn series_on_all(&self, closures: Vec<SeriesItem>) {
|
||||
for (_, category) in self.categories.iter() {
|
||||
for (_, repo) in category.repos.iter() {
|
||||
for instruction in closures.iter() {
|
||||
let f = &instruction.closure;
|
||||
let op = instruction.operation;
|
||||
let mut sp =
|
||||
Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into());
|
||||
if f(repo) {
|
||||
sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into());
|
||||
} else {
|
||||
sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Runs associated function on all repos in config
|
||||
///
|
||||
/// Unlike `series_on_all`, this does not stop if it encounters an error
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// Here is an example of how an associated method could use this function.
|
||||
///
|
||||
/// ```
|
||||
/// let series: Vec<SeriesItem> = vec![
|
||||
/// SeriesItem {
|
||||
/// operation: "pull",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.pull()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "add",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.add_all()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "commit",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.commit()),
|
||||
/// },
|
||||
/// SeriesItem {
|
||||
/// operation: "push",
|
||||
/// closure: Box::new(move |repo: &GitRepo| repo.push()),
|
||||
/// },
|
||||
/// ];
|
||||
/// self.all_on_all(series);
|
||||
/// ```
|
||||
pub fn all_on_all(&self, closures: Vec<SeriesItem>) {
|
||||
for (_, category) in self.categories.iter() {
|
||||
for (_, repo) in category.repos.iter() {
|
||||
for instruction in closures.iter() {
|
||||
let f = &instruction.closure;
|
||||
let op = instruction.operation;
|
||||
let mut sp =
|
||||
Spinner::new(Spinners::Dots10, format!("{}: {}", repo.name, op).into());
|
||||
if f(repo) {
|
||||
sp.stop_and_persist("✔", format!("{}: {}", repo.name, op).into());
|
||||
} else {
|
||||
sp.stop_and_persist("❎", format!("{}: {}", repo.name, op).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Tries to pull all repositories, skips if fail.
|
||||
pub fn pull_all(&self) {
|
||||
debug!("exectuting pull_all");
|
||||
for r in self.repos.iter() {
|
||||
r.pull();
|
||||
}
|
||||
self.on_all_spinner("pull", |repo| repo.pull());
|
||||
}
|
||||
/// Tries to clone all repositories, skips if fail.
|
||||
pub fn clone_all(&self) {
|
||||
debug!("exectuting clone_all");
|
||||
for r in self.repos.iter() {
|
||||
r.clone();
|
||||
}
|
||||
self.on_all_spinner("clone", |repo| repo.clone());
|
||||
}
|
||||
/// Tries to add all work in all repositories, skips if fail.
|
||||
pub fn add_all(&self) {
|
||||
debug!("exectuting clone_all");
|
||||
for r in self.repos.iter() {
|
||||
r.add_all();
|
||||
}
|
||||
self.on_all_spinner("add", |repo| repo.add_all());
|
||||
}
|
||||
/// Tries to commit all repositories one at a time, skips if fail.
|
||||
pub fn commit_all(&self) {
|
||||
debug!("exectuting clone_all");
|
||||
for r in self.repos.iter() {
|
||||
r.commit();
|
||||
}
|
||||
self.on_all_spinner("commit", |repo| repo.commit());
|
||||
}
|
||||
/// Tries to commit all repositories with msg, skips if fail.
|
||||
pub fn commit_all_msg(&self, msg: &String) {
|
||||
pub fn commit_all_msg(&self, msg: &str) {
|
||||
debug!("exectuting clone_all");
|
||||
for r in self.repos.iter() {
|
||||
r.commit_with_msg(msg);
|
||||
}
|
||||
self.on_all_spinner("commit", |repo| repo.commit_with_msg(msg));
|
||||
}
|
||||
/// Tries to pull, add all, commit with msg "quick commit", and push all
|
||||
/// repositories, skips if fail.
|
||||
pub fn quick(&self, msg: &String) {
|
||||
pub fn quick(&self, msg: &'static str) {
|
||||
debug!("exectuting quick");
|
||||
for r in self.repos.iter() {
|
||||
r.pull();
|
||||
r.add_all();
|
||||
r.commit_with_msg(msg);
|
||||
r.push();
|
||||
let series: Vec<SeriesItem> = vec![
|
||||
SeriesItem {
|
||||
operation: "pull",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.pull()),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "add",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.add_all()),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "commit",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.commit_with_msg(msg)),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "push",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.push()),
|
||||
},
|
||||
];
|
||||
self.all_on_all(series);
|
||||
}
|
||||
/// Tries to pull, add all, commit with msg "quick commit", and push all
|
||||
/// repositories, skips if fail.
|
||||
pub fn fast(&self, msg: &'static str) {
|
||||
debug!("exectuting fast");
|
||||
let series: Vec<SeriesItem> = vec![
|
||||
SeriesItem {
|
||||
operation: "pull",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.pull()),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "add",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.add_all()),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "commit",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.commit()),
|
||||
},
|
||||
SeriesItem {
|
||||
operation: "push",
|
||||
closure: Box::new(move |repo: &GitRepo| repo.push()),
|
||||
},
|
||||
];
|
||||
self.series_on_all(series);
|
||||
}
|
||||
|
||||
/* LINK RELATED */
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Tries to link all repositories, skips if fail.
|
||||
pub fn link_all(&self) {
|
||||
debug!("exectuting link_all");
|
||||
|
|
129
src/main.rs
129
src/main.rs
|
@ -13,6 +13,23 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! A Rust GitOps/symlinkfarm orchestrator inspired by GNU Stow.
|
||||
//!
|
||||
//! # What is?
|
||||
//!
|
||||
//! A Rust GitOps/symlinkfarm orchestrator inspired by GNU Stow. Useful for dealing
|
||||
//! with "dotfiles", and with git support as a first class feature. Configuration is
|
||||
//! done throug a single yaml file, giving it a paradigm that should bring joy to
|
||||
//! 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.
|
||||
//!
|
||||
//! That said, we're in 0.0.Z, *here be dragons* for now.
|
||||
#![feature(unsized_tuple_coercion)]
|
||||
|
||||
extern crate log;
|
||||
extern crate pretty_env_logger;
|
||||
|
@ -26,14 +43,20 @@ mod utils;
|
|||
|
||||
use cli::{Args, Commands};
|
||||
use git::Config;
|
||||
use utils::strings::{FAST_COMMIT, QUICK_COMMIT};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[allow(unused)]
|
||||
use log::{debug, error, info, trace, warn};
|
||||
|
||||
/// The main loop of the binary
|
||||
///
|
||||
/// Here, we handle parsing the configuration file, as well as matching commands
|
||||
/// to the relavant operations.
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
let args = Args::parse();
|
||||
let mut args = Args::parse();
|
||||
let config = Config::new(&args.config);
|
||||
match &args {
|
||||
args if args.license => println!("{}", utils::strings::INTERACTIVE_LICENSE),
|
||||
|
@ -41,12 +64,27 @@ fn main() {
|
|||
args if args.code_of_conduct => unimplemented!(),
|
||||
_ => (),
|
||||
}
|
||||
match &args.command {
|
||||
match &mut args.command {
|
||||
Some(Commands::Link { msg: _ }) => {
|
||||
config.link_all();
|
||||
}
|
||||
Some(Commands::Quick { msg }) => {
|
||||
config.quick(msg.as_ref().get_or_insert(&"gg: quick commit".to_string()));
|
||||
let s = Box::leak(
|
||||
msg.as_mut()
|
||||
.get_or_insert(&mut QUICK_COMMIT.to_string())
|
||||
.clone()
|
||||
.into_boxed_str(),
|
||||
);
|
||||
config.quick(s);
|
||||
}
|
||||
Some(Commands::Fast { msg }) => {
|
||||
let s = Box::leak(
|
||||
msg.as_mut()
|
||||
.get_or_insert(&mut FAST_COMMIT.to_string())
|
||||
.clone()
|
||||
.into_boxed_str(),
|
||||
);
|
||||
config.fast(s);
|
||||
}
|
||||
Some(Commands::Clone { msg: _ }) => {
|
||||
config.clone_all();
|
||||
|
@ -71,35 +109,51 @@ fn main() {
|
|||
#[cfg(test)]
|
||||
mod config {
|
||||
use crate::*;
|
||||
use git::GitRepo;
|
||||
use git::RepoFlags::{Clone, Push};
|
||||
use git::{Category, GitRepo};
|
||||
use relative_path::RelativePath;
|
||||
use std::collections::HashMap;
|
||||
use std::env::current_dir;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
#[test]
|
||||
fn init_config() {
|
||||
let _config = Config {
|
||||
repos: vec![],
|
||||
categories: HashMap::new(),
|
||||
links: vec![],
|
||||
};
|
||||
}
|
||||
#[test]
|
||||
fn init_config_populate() {
|
||||
let default_category = Category {
|
||||
flags: vec![],
|
||||
repos: HashMap::new(),
|
||||
};
|
||||
let mut config = Config {
|
||||
repos: vec![],
|
||||
categories: HashMap::new(),
|
||||
links: vec![],
|
||||
};
|
||||
for _ in 0..=5 {
|
||||
let repo = GitRepo {
|
||||
config
|
||||
.categories
|
||||
.insert(format!("{}", 0).to_string(), default_category);
|
||||
for i in 0..=5 {
|
||||
config
|
||||
.categories
|
||||
.get_mut(&format!("{}", 0).to_string())
|
||||
.expect("category not found")
|
||||
.repos
|
||||
.insert(
|
||||
format!("{}", i).to_string(),
|
||||
GitRepo {
|
||||
name: "test repo".to_string(),
|
||||
path: "/tmp".to_string(),
|
||||
url: "https://github.com/cafkafk/gg".to_string(),
|
||||
clone: false,
|
||||
};
|
||||
config.repos.push(repo);
|
||||
flags: vec![Clone, Push],
|
||||
},
|
||||
);
|
||||
}
|
||||
let yaml = serde_yaml::to_string(&config).unwrap();
|
||||
println!("{}", yaml);
|
||||
// let yaml = serde_yaml::to_string(&config).unwrap();
|
||||
// println!("{}", yaml);
|
||||
}
|
||||
#[test]
|
||||
fn read_config_populate() {
|
||||
|
@ -130,8 +184,23 @@ mod config {
|
|||
let test_config = Config::new(&RelativePath::new("./src/test/test.yaml").to_string());
|
||||
assert_eq!(config, test_config);
|
||||
}
|
||||
fn get_category<'cat>(config: &'cat Config, name: &'cat str) -> &'cat Category {
|
||||
config.categories.get(name).expect("failed to get category")
|
||||
}
|
||||
fn get_repo<F>(config: &Config, cat_name: &str, repo_name: &str, f: F)
|
||||
where
|
||||
F: FnOnce(&GitRepo),
|
||||
{
|
||||
f(config
|
||||
.categories
|
||||
.get(cat_name)
|
||||
.expect("failed to get category")
|
||||
.repos
|
||||
.get(repo_name)
|
||||
.expect("failed to get category"))
|
||||
}
|
||||
#[test]
|
||||
fn read_and_verify_config() {
|
||||
fn is_config_readable() {
|
||||
let root = current_dir().unwrap();
|
||||
let config = Config::new(
|
||||
&RelativePath::new("./src/test/config.yaml")
|
||||
|
@ -140,31 +209,16 @@ mod config {
|
|||
.into_string()
|
||||
.unwrap(),
|
||||
);
|
||||
// FIXME This is unnecessarily terse
|
||||
|
||||
let flags = vec![Clone, Push];
|
||||
// FIXME not very extensive
|
||||
#[allow(clippy::bool_assert_comparison)]
|
||||
{
|
||||
assert_eq!(config.repos[0].name, "gg");
|
||||
assert_eq!(config.repos[0].path, "/home/ces/.dots/");
|
||||
assert_eq!(config.repos[0].url, "git@github.com:cafkafk/gg.git");
|
||||
assert_eq!(config.repos[0].clone, true);
|
||||
assert_eq!(config.repos[1].name, "li");
|
||||
assert_eq!(config.repos[1].path, "/home/ces/org/src/git/");
|
||||
assert_eq!(config.repos[1].url, "git@github.com:cafkafk/li.git");
|
||||
assert_eq!(config.repos[1].clone, true);
|
||||
assert_eq!(config.repos[2].name, "qmk_firmware");
|
||||
assert_eq!(config.repos[2].path, "/home/ces/org/src/git/");
|
||||
assert_eq!(
|
||||
config.repos[2].url,
|
||||
"git@github.com:cafkafk/qmk_firmware.git"
|
||||
);
|
||||
assert_eq!(config.repos[2].clone, true);
|
||||
assert_eq!(config.repos[3].name, "starship");
|
||||
assert_eq!(config.repos[3].path, "/home/ces/org/src/git/");
|
||||
assert_eq!(
|
||||
config.repos[3].url,
|
||||
"https://github.com/starship/starship.git"
|
||||
);
|
||||
assert_eq!(config.repos[3].clone, true);
|
||||
get_repo(&config, "config", "qmk_firmware", |repo| {
|
||||
assert_eq!(repo.name, "qmk_firmware");
|
||||
assert_eq!(repo.path, "/home/ces/org/src/git/");
|
||||
assert_eq!(repo.url, "git@github.com:cafkafk/qmk_firmware.git");
|
||||
})
|
||||
}
|
||||
{
|
||||
assert_eq!(config.links[0].name, "gg");
|
||||
|
@ -173,6 +227,7 @@ mod config {
|
|||
assert_eq!(config.links[1].name, "starship");
|
||||
assert_eq!(config.links[1].rx, "/home/ces/.config/starship.toml");
|
||||
assert_eq!(config.links[1].tx, "/home/ces/.dots/starship.toml");
|
||||
// FIXME doesn't check repoflags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
repos:
|
||||
- name: gg
|
||||
path: /home/ces/.dots/
|
||||
url: git@github.com:cafkafk/gg.git
|
||||
clone: true
|
||||
- name: li
|
||||
path: /home/ces/org/src/git/
|
||||
url: git@github.com:cafkafk/li.git
|
||||
clone: true
|
||||
- name: qmk_firmware
|
||||
categories:
|
||||
config:
|
||||
flags: []
|
||||
repos:
|
||||
qmk_firmware:
|
||||
name: qmk_firmware
|
||||
path: /home/ces/org/src/git/
|
||||
url: git@github.com:cafkafk/qmk_firmware.git
|
||||
clone: true
|
||||
- name: starship
|
||||
flags: [Clone, Push]
|
||||
starship:
|
||||
name: starship
|
||||
path: /home/ces/org/src/git/
|
||||
url: https://github.com/starship/starship.git
|
||||
clone: true
|
||||
flags: [Clone, Push]
|
||||
utils:
|
||||
flags: []
|
||||
repos:
|
||||
gg:
|
||||
name: gg
|
||||
path: /home/ces/.dots/
|
||||
url: git@github.com:cafkafk/gg.git
|
||||
flags: [Clone, Push]
|
||||
li:
|
||||
name: li
|
||||
path: /home/ces/org/src/git/
|
||||
url: git@github.com:cafkafk/li.git
|
||||
flags: [Clone, Push]
|
||||
links:
|
||||
- name: gg
|
||||
rx: /home/ces/.config/gg
|
||||
|
|
|
@ -1,20 +1,38 @@
|
|||
repos:
|
||||
- name: gg
|
||||
path: /home/ces/.dots/
|
||||
url: git@github.com:cafkafk/gg.git
|
||||
clone: true
|
||||
- name: li
|
||||
categories:
|
||||
utils:
|
||||
flags: []
|
||||
repos:
|
||||
li:
|
||||
name: li
|
||||
path: /home/ces/org/src/git/
|
||||
url: git@github.com:cafkafk/li.git
|
||||
clone: true
|
||||
- name: qmk_firmware
|
||||
flags:
|
||||
- Clone
|
||||
- Push
|
||||
gg:
|
||||
name: gg
|
||||
path: /home/ces/.dots/
|
||||
url: git@github.com:cafkafk/gg.git
|
||||
flags:
|
||||
- Clone
|
||||
- Push
|
||||
config:
|
||||
flags: []
|
||||
repos:
|
||||
qmk_firmware:
|
||||
name: qmk_firmware
|
||||
path: /home/ces/org/src/git/
|
||||
url: git@github.com:cafkafk/qmk_firmware.git
|
||||
clone: true
|
||||
- name: starship
|
||||
flags:
|
||||
- Clone
|
||||
- Push
|
||||
starship:
|
||||
name: starship
|
||||
path: /home/ces/org/src/git/
|
||||
url: https://github.com/starship/starship.git
|
||||
clone: true
|
||||
flags:
|
||||
- Clone
|
||||
- Push
|
||||
links:
|
||||
- name: gg
|
||||
rx: /home/ces/.config/gg
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! Sublibrary for useful functions
|
||||
|
||||
pub mod dir;
|
||||
pub mod strings;
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! Nice helpers for dealing with filesystem environment.
|
||||
|
||||
#![feature(stmt_expr_attributes)]
|
||||
use log::{debug, error, info, trace, warn};
|
||||
|
@ -20,6 +22,9 @@ use log::{debug, error, info, trace, warn};
|
|||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
/// Returns the users current dir
|
||||
///
|
||||
/// Does not work on Windows
|
||||
pub fn current_dir() -> String {
|
||||
#[allow(deprecated)] // NOTE we don't care about windows , we don't support it
|
||||
env::current_dir()
|
||||
|
@ -29,6 +34,9 @@ pub fn current_dir() -> String {
|
|||
.expect("Failed to turn home_dir into a valid string")
|
||||
}
|
||||
|
||||
/// Returns the users home dir
|
||||
///
|
||||
/// Does not work on Windows
|
||||
pub fn home_dir() -> String {
|
||||
#[allow(deprecated)] // NOTE we don't care about windows , we don't support it
|
||||
env::home_dir()
|
||||
|
|
|
@ -13,14 +13,19 @@
|
|||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see https://www.gnu.org/gpl-3.0.html.
|
||||
//
|
||||
//! Module for chunk of text
|
||||
//!
|
||||
//! Ideally, at a VERY long term scale, this should be a nice pattern for
|
||||
//! possible translations.
|
||||
|
||||
/// Contains the notice for interactive programs from the GPLv3's "How to Apply
|
||||
/// These Terms to Your New Programs"
|
||||
pub const INTERACTIVE_NOTICE: &str = "\
|
||||
gg Copyright (C) 2023 Christina Sørensen <christina@cafkafk.com>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `gg --warranty'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
under certain conditions; type `gg --license' for details.
|
||||
";
|
||||
|
||||
/// Contains the license part of the long notice for interactive programs from
|
||||
|
@ -40,3 +45,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
";
|
||||
|
||||
/// 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";
|
||||
|
|
Loading…
Reference in a new issue