feat!: put links in categories

This required a lot of refatoring, and added a lot of codedepth that
have to be repaid soon by a large refactor.

Also, it is becoming painfully obvious that testing should be expanded
significantly.

This, after the refactor, nonetheless.

Signed-off-by: Christina Sørensen <christina@cafkafk.com>
This commit is contained in:
Christina Sørensen 2023-07-04 11:26:25 +02:00 committed by Christina Sørensen
parent c62a0a720f
commit d5b845508a
5 changed files with 109 additions and 43 deletions

View file

@ -1,7 +1,9 @@
#+title: Roadmap #+title: Roadmap
* 0.2.0 (maybe) * 0.2.0 (maybe)
- [ ] Links in categories? - [-] Links in categories?
- [ ] Fix category with no links
- [ ] Refactor
* 0.1.2 * 0.1.2
- [X] Implement Quiet flag - [X] Implement Quiet flag
* 0.1.1 * 0.1.1

View file

@ -62,8 +62,6 @@ pub struct Config {
/// ///
/// Key should conceptually be seen as the name of the category. /// Key should conceptually be seen as the name of the category.
pub categories: HashMap<String, Category>, pub categories: HashMap<String, Category>,
/// A vector containing links
pub links: Vec<Links>,
} }
/// Represents a category of repositories /// Represents a category of repositories
@ -73,16 +71,22 @@ pub struct Config {
pub struct Category { pub struct Category {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub flags: Option<Vec<RepoFlags>>, // FIXME: not implemented pub flags: Option<Vec<RepoFlags>>, // FIXME: not implemented
/// map of all categories /// map of all repos in category
/// ///
/// Key should conceptually be seen as the name of the category. /// Key should conceptually be seen as the name of the category.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub repos: Option<HashMap<String, GitRepo>>, pub repos: Option<HashMap<String, GitRepo>>,
/// map of all links in category
///
/// Key should conceptually be seen as the name of the category.
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<HashMap<String, Link>>,
} }
/// Contain fields for a single link. /// Contain fields for a single link.
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct Links { pub struct Link {
/// The name of the link /// The name of the link
pub name: String, pub name: String,
pub rx: String, pub rx: String,
@ -115,7 +119,7 @@ pub struct SeriesItem<'series> {
//////////////////////////////////// ////////////////////////////////////
//////////////////////////////////// ////////////////////////////////////
fn handle_file_exists(selff: &Links, tx_path: &Path, rx_path: &Path) { fn handle_file_exists(selff: &Link, tx_path: &Path, rx_path: &Path) -> bool {
match rx_path.read_link() { match rx_path.read_link() {
Ok(file) Ok(file)
if file.canonicalize().expect("failed to canonicalize file") if file.canonicalize().expect("failed to canonicalize file")
@ -125,22 +129,25 @@ fn handle_file_exists(selff: &Links, tx_path: &Path, rx_path: &Path) {
"Linking {} -> {} failed: file already linked", "Linking {} -> {} failed: file already linked",
&selff.tx, &selff.rx &selff.tx, &selff.rx
); );
false
} }
Ok(file) => { Ok(file) => {
error!( error!(
"Linking {} -> {} failed: link to different file exists", "Linking {} -> {} failed: link to different file exists",
&selff.tx, &selff.rx &selff.tx, &selff.rx
); );
false
} }
Err(error) => { Err(error) => {
error!("Linking {} -> {} failed: file exists", &selff.tx, &selff.rx); error!("Linking {} -> {} failed: file exists", &selff.tx, &selff.rx);
false
} }
} }
} }
impl Links { impl Link {
/// Creates the link from the link struct /// Creates the link from the link struct
pub fn link(&self) { pub fn link(&self) -> bool {
let tx_path: &Path = std::path::Path::new(&self.tx); let tx_path: &Path = std::path::Path::new(&self.tx);
let rx_path: &Path = std::path::Path::new(&self.rx); let rx_path: &Path = std::path::Path::new(&self.rx);
match rx_path.try_exists() { match rx_path.try_exists() {
@ -150,14 +157,17 @@ impl Links {
"Linking {} -> {} failed: broken symlink", "Linking {} -> {} failed: broken symlink",
&self.tx, &self.rx &self.tx, &self.rx
); );
false
} }
Ok(false) => { Ok(false) => {
symlink(&self.tx, &self.rx).expect("failed to create link"); symlink(&self.tx, &self.rx).expect("failed to create link");
true
} }
Err(error) => { Err(error) => {
error!("Linking {} -> {} failed: {}", &self.tx, &self.rx, error); error!("Linking {} -> {} failed: {}", &self.tx, &self.rx, error);
false
}
} }
};
} }
} }
@ -363,6 +373,38 @@ impl Config {
} }
} }
} }
/// Runs associated function on all links in config
fn on_all_links_spinner<F>(&self, op: &str, f: F)
where
F: Fn(&Link) -> bool,
{
for category in self.categories.values() {
match category.links.as_ref() {
Some(links) => {
for (_, link) in links.iter() {
if !settings::QUIET.load(std::sync::atomic::Ordering::Relaxed) {
let mut sp =
Spinner::new(Spinners::Dots10, format!("{}: {}", link.name, op));
if f(link) {
sp.stop_and_persist(
success_str(),
format!("{}: {}", link.name, op),
);
} else {
sp.stop_and_persist(
failure_str(),
format!("{}: {}", link.name, op),
);
}
} else {
f(link);
}
}
}
None => continue,
};
}
}
/// Runs associated function on all repos in config /// Runs associated function on all repos in config
/// ///
/// TODO: need to be made over a generic repo type /// TODO: need to be made over a generic repo type
@ -558,8 +600,6 @@ impl Config {
/// Tries to link all repositories, skips if fail. /// Tries to link all repositories, skips if fail.
pub fn link_all(&self) { pub fn link_all(&self) {
debug!("exectuting link_all"); debug!("exectuting link_all");
for link in &self.links { self.on_all_links_spinner("link", Link::link);
link.link();
}
} }
} }

View file

@ -116,7 +116,7 @@ fn main() {
mod config { mod config {
use crate::*; use crate::*;
use git::RepoFlags::{Clone, Push}; use git::RepoFlags::{Clone, Push};
use git::{Category, GitRepo}; use git::{Category, GitRepo, Link};
use relative_path::RelativePath; use relative_path::RelativePath;
use std::collections::HashMap; use std::collections::HashMap;
use std::env::current_dir; use std::env::current_dir;
@ -126,7 +126,6 @@ mod config {
fn init_config() { fn init_config() {
let _config = Config { let _config = Config {
categories: HashMap::new(), categories: HashMap::new(),
links: vec![],
}; };
} }
#[test] #[test]
@ -134,10 +133,10 @@ mod config {
let default_category = Category { let default_category = Category {
flags: Some(vec![]), flags: Some(vec![]),
repos: Some(HashMap::new()), repos: Some(HashMap::new()),
links: Some(HashMap::new()),
}; };
let mut config = Config { let mut config = Config {
categories: HashMap::new(), categories: HashMap::new(),
links: vec![],
}; };
config config
.categories .categories
@ -192,6 +191,7 @@ mod config {
let test_config = Config::new(&RelativePath::new("./src/test/test.yaml").to_string()); let test_config = Config::new(&RelativePath::new("./src/test/test.yaml").to_string());
assert_eq!(config, test_config); assert_eq!(config, test_config);
} }
#[allow(dead_code)]
fn get_category<'cat>(config: &'cat Config, name: &'cat str) -> &'cat Category { fn get_category<'cat>(config: &'cat Config, name: &'cat str) -> &'cat Category {
config.categories.get(name).expect("failed to get category") config.categories.get(name).expect("failed to get category")
} }
@ -209,6 +209,20 @@ mod config {
.get(repo_name) .get(repo_name)
.expect("failed to get category")) .expect("failed to get category"))
} }
fn get_link<F>(config: &Config, cat_name: &str, link_name: &str, f: F)
where
F: FnOnce(&Link),
{
f(config
.categories
.get(cat_name)
.expect("failed to get category")
.links
.as_ref()
.expect("failed to get repo")
.get(link_name)
.expect("failed to get category"))
}
#[test] #[test]
fn is_config_readable() { fn is_config_readable() {
let root = current_dir().expect("failed to get current dir"); let root = current_dir().expect("failed to get current dir");
@ -220,7 +234,7 @@ mod config {
.expect("failed to turnn config into string"), .expect("failed to turnn config into string"),
); );
let flags = vec![Clone, Push]; let _flags = vec![Clone, Push];
// FIXME not very extensive // FIXME not very extensive
#[allow(clippy::bool_assert_comparison)] #[allow(clippy::bool_assert_comparison)]
{ {
@ -228,8 +242,14 @@ mod config {
assert_eq!(repo.name, "qmk_firmware"); assert_eq!(repo.name, "qmk_firmware");
assert_eq!(repo.path, "/home/ces/org/src/git/"); assert_eq!(repo.path, "/home/ces/org/src/git/");
assert_eq!(repo.url, "git@github.com:cafkafk/qmk_firmware.git"); assert_eq!(repo.url, "git@github.com:cafkafk/qmk_firmware.git");
}) });
get_link(&config, "stuff", "gg", |link| {
assert_eq!(link.name, "gg");
assert_eq!(link.tx, "/home/ces/.dots/gg");
assert_eq!(link.rx, "/home/ces/.config/gg");
});
} }
/*
{ {
assert_eq!(config.links[0].name, "gg"); assert_eq!(config.links[0].name, "gg");
assert_eq!(config.links[0].rx, "/home/ces/.config/gg"); assert_eq!(config.links[0].rx, "/home/ces/.config/gg");
@ -238,7 +258,7 @@ mod config {
assert_eq!(config.links[1].rx, "/home/ces/.config/starship.toml"); assert_eq!(config.links[1].rx, "/home/ces/.config/starship.toml");
assert_eq!(config.links[1].tx, "/home/ces/.dots/starship.toml"); assert_eq!(config.links[1].tx, "/home/ces/.dots/starship.toml");
// FIXME doesn't check repoflags // FIXME doesn't check repoflags
} }*/
} }
} }

View file

@ -36,10 +36,12 @@ categories:
name: li name: li
path: /home/ces/org/src/git/ path: /home/ces/org/src/git/
url: git@github.com:cafkafk/li.git url: git@github.com:cafkafk/li.git
links: links:
- name: gg gg:
name: gg
rx: /home/ces/.config/gg rx: /home/ces/.config/gg
tx: /home/ces/.dots/gg tx: /home/ces/.dots/gg
- name: starship starship:
name: starship
rx: /home/ces/.config/starship.toml rx: /home/ces/.config/starship.toml
tx: /home/ces/.dots/starship.toml tx: /home/ces/.dots/starship.toml

View file

@ -2,13 +2,6 @@ categories:
config: config:
flags: [] flags: []
repos: repos:
starship:
name: starship
path: /home/ces/org/src/git/
url: https://github.com/starship/starship.git
flags:
- Clone
- Push
qmk_firmware: qmk_firmware:
name: qmk_firmware name: qmk_firmware
path: /home/ces/org/src/git/ path: /home/ces/org/src/git/
@ -16,17 +9,33 @@ categories:
flags: flags:
- Clone - Clone
- Push - Push
starship:
name: starship
path: /home/ces/org/src/git/
url: https://github.com/starship/starship.git
flags:
- Clone
- Push
stuff: stuff:
flags: [] flags: []
repos: repos:
li:
name: li
path: /home/ces/org/src/git/
url: git@github.com:cafkafk/li.git
gg: gg:
name: gg name: gg
path: /home/ces/.dots/ path: /home/ces/.dots/
url: git@github.com:cafkafk/gg.git url: git@github.com:cafkafk/gg.git
li:
name: li
path: /home/ces/org/src/git/
url: git@github.com:cafkafk/li.git
links:
gg:
name: gg
rx: /home/ces/.config/gg
tx: /home/ces/.dots/gg
starship:
name: starship
rx: /home/ces/.config/starship.toml
tx: /home/ces/.dots/starship.toml
utils: utils:
repos: repos:
gg: gg:
@ -44,10 +53,3 @@ categories:
- Clone - Clone
- Push - Push
empty: {} empty: {}
links:
- name: gg
rx: /home/ces/.config/gg
tx: /home/ces/.dots/gg
- name: starship
rx: /home/ces/.config/starship.toml
tx: /home/ces/.dots/starship.toml