Something went wrong. Try again.
Opinionated Android 15+ Linux Terminal Setup
android linux command-line-tools
Something went wrong. Try again.
13 kB · 369 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370use owo_colors::OwoColorize;use std::{collections::HashMap, fmt};
use crate::config::{Configuration, OhMyPosh, SshConfig};
#[derive(Debug)]pub enum Diff { Added(String, String, String), // Parent, child, value Removed(String, String, String), // Parent, child, value Changed(String, String, String, String), // Parent, child, old value, new value Nested(String, Vec<Diff>), // Parent field, nested differences}
impl fmt::Display for Diff { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Diff::Added(parent, child, value) => { if child.is_empty() { write!(f, "+ {}: {}", parent.green(), value.green()) } else { write!( f, "+ {}:\n + {}: {}", parent.green(), child.green(), value.green() ) } } Diff::Removed(parent, child, value) => { if child.is_empty() { write!(f, "- {}: {}", parent.magenta(), value.magenta()) } else { write!( f, "- {}:\n - {}: {}", parent.magenta(), child.magenta(), value.magenta() ) } } Diff::Changed(parent, child, old_value, new_value) => { if child.is_empty() { write!( f, "- {}: {}\n+ {}: {}", parent.magenta(), old_value.magenta(), parent.green(), new_value.green() ) } else { write!( f, "- {}:\n - {}: {}\n+ {}:\n + {}: {}", parent.magenta(), child.magenta(), old_value.magenta(), parent.green(), child.green(), new_value.green() ) } } Diff::Nested(parent, diffs) => { write!(f, "{}:", parent)?; for diff in diffs { write!(f, "\n {}", diff)?; } Ok(()) } } }}
fn compare_hashmap( parent: &str, old: &Option<HashMap<String, String>>, new: &Option<HashMap<String, String>>,) -> Vec<Diff> { let mut diffs = Vec::new(); match (old, new) { (None, Some(new_map)) => { for (key, value) in new_map { diffs.push(Diff::Added(parent.to_string(), key.clone(), value.clone())); } } (Some(old_map), None) => { for (key, value) in old_map { diffs.push(Diff::Removed( parent.to_string(), key.clone(), value.clone(), )); } } (Some(old_map), Some(new_map)) => { for (key, old_value) in old_map { match new_map.get(key) { None => diffs.push(Diff::Removed( parent.to_string(), key.clone(), old_value.clone(), )), Some(new_value) if new_value != old_value => { diffs.push(Diff::Changed( parent.to_string(), key.clone(), old_value.clone(), new_value.clone(), )); } _ => {} } } for (key, new_value) in new_map { if !old_map.contains_key(key) { diffs.push(Diff::Added( parent.to_string(), key.clone(), new_value.clone(), )); } } } (None, None) => {} } diffs}
fn compare_vec(parent: &str, old: &Option<Vec<String>>, new: &Option<Vec<String>>) -> Vec<Diff> { let mut diffs = Vec::new(); match (old, new) { (None, Some(new_vec)) => { for item in new_vec { diffs.push(Diff::Added( parent.to_string(), "".to_string(), item.clone(), )); } } (Some(old_vec), None) => { for item in old_vec { diffs.push(Diff::Removed( parent.to_string(), "".to_string(), item.clone(), )); } } (Some(old_vec), Some(new_vec)) => { let old_set: std::collections::HashSet<_> = old_vec.iter().collect(); let new_set: std::collections::HashSet<_> = new_vec.iter().collect(); for item in new_set.difference(&old_set) { diffs.push(Diff::Added( parent.to_string(), "".to_string(), item.to_string(), )); } for item in old_set.difference(&new_set) { diffs.push(Diff::Removed( parent.to_string(), "".to_string(), item.to_string(), )); } } (None, None) => {} } diffs}
fn compare_bool(parent: &str, old: &Option<bool>, new: &Option<bool>) -> Vec<Diff> { match (old, new) { (None, Some(new_val)) => vec![Diff::Added( parent.to_string(), "".to_string(), new_val.to_string(), )], (Some(old_val), None) => vec![Diff::Removed( parent.to_string(), "".to_string(), old_val.to_string(), )], (Some(old_val), Some(new_val)) if old_val != new_val => { vec![Diff::Changed( parent.to_string(), "".to_string(), old_val.to_string(), new_val.to_string(), )] } _ => vec![], }}
fn compare_oh_my_posh(old: &Option<OhMyPosh>, new: &Option<OhMyPosh>) -> Vec<Diff> { let mut diffs = Vec::new(); match (old, new) { (None, Some(new_omp)) => { if let Some(theme) = &new_omp.theme { diffs.push(Diff::Added( "oh_my_posh".to_string(), "theme".to_string(), theme.clone(), )); } } (Some(old_omp), None) => { if let Some(theme) = &old_omp.theme { diffs.push(Diff::Removed( "oh_my_posh".to_string(), "theme".to_string(), theme.clone(), )); } } (Some(old_omp), Some(new_omp)) => match (&old_omp.theme, &new_omp.theme) { (None, Some(new_theme)) => { diffs.push(Diff::Added( "oh_my_posh".to_string(), "theme".to_string(), new_theme.clone(), )); } (Some(old_theme), None) => { diffs.push(Diff::Removed( "oh_my_posh".to_string(), "theme".to_string(), old_theme.clone(), )); } (Some(old_theme), Some(new_theme)) if old_theme != new_theme => { diffs.push(Diff::Changed( "oh_my_posh".to_string(), "theme".to_string(), old_theme.clone(), new_theme.clone(), )); } _ => {} }, (None, None) => {} } if !diffs.is_empty() { vec![Diff::Nested("oh_my_posh".to_string(), diffs)] } else { vec![] }}
fn compare_ssh_config(old: &Option<SshConfig>, new: &Option<SshConfig>) -> Vec<Diff> { let mut diffs = Vec::new(); match (old, new) { (None, Some(new_ssh)) => { if let Some(port) = new_ssh.port { diffs.push(Diff::Added( "ssh".to_string(), "port".to_string(), port.to_string(), )); } if let Some(keys) = &new_ssh.authorized_keys { for key in keys { diffs.push(Diff::Added( "ssh".to_string(), "authorized_keys".to_string(), key.clone(), )); } } } (Some(old_ssh), None) => { if let Some(port) = old_ssh.port { diffs.push(Diff::Removed( "ssh".to_string(), "port".to_string(), port.to_string(), )); } if let Some(keys) = &old_ssh.authorized_keys { for key in keys { diffs.push(Diff::Removed( "ssh".to_string(), "authorized_keys".to_string(), key.clone(), )); } } } (Some(old_ssh), Some(new_ssh)) => { match (old_ssh.port, new_ssh.port) { (None, Some(new_port)) => { diffs.push(Diff::Added( "ssh".to_string(), "port".to_string(), new_port.to_string(), )); } (Some(old_port), None) => { diffs.push(Diff::Removed( "ssh".to_string(), "port".to_string(), old_port.to_string(), )); } (Some(old_port), Some(new_port)) if old_port != new_port => { diffs.push(Diff::Changed( "ssh".to_string(), "port".to_string(), old_port.to_string(), new_port.to_string(), )); } _ => {} } let key_diffs = compare_vec("ssh", &old_ssh.authorized_keys, &new_ssh.authorized_keys); diffs.extend(key_diffs.into_iter().map(|diff| match diff { Diff::Added(_, _, value) => { Diff::Added("ssh".to_string(), "authorized_keys".to_string(), value) } Diff::Removed(_, _, value) => { Diff::Removed("ssh".to_string(), "authorized_keys".to_string(), value) } Diff::Changed(_, _, old_value, new_value) => Diff::Changed( "ssh".to_string(), "authorized_keys".to_string(), old_value, new_value, ), Diff::Nested(_, _) => diff, // Unreachable })); } (None, None) => {} } if !diffs.is_empty() { vec![Diff::Nested("ssh".to_string(), diffs)] } else { vec![] }}
pub fn compare_configurations(old: &Configuration, new: &Configuration) -> Vec<Diff> { let mut diffs = Vec::new();
diffs.extend(compare_hashmap("stow", &old.stow, &new.stow)); diffs.extend(compare_hashmap("mise", &old.mise, &new.mise)); diffs.extend(compare_hashmap("nix", &old.nix, &new.nix)); diffs.extend(compare_hashmap("pkgx", &old.pkgx, &new.pkgx)); diffs.extend(compare_hashmap("curl", &old.curl, &new.curl)); diffs.extend(compare_hashmap("alias", &old.alias, &new.alias)); diffs.extend(compare_hashmap("npm", &old.npm, &new.npm));
diffs.extend(compare_vec("apt-get", &old.apt_get, &new.apt_get));
diffs.extend(compare_bool("blesh", &old.blesh, &new.blesh)); diffs.extend(compare_bool("zoxide", &old.zoxide, &new.zoxide)); diffs.extend(compare_bool("tailscale", &old.tailscale, &new.tailscale)); diffs.extend(compare_bool("neofetch", &old.neofetch, &new.neofetch)); diffs.extend(compare_bool("doppler", &old.doppler, &new.doppler));
diffs.extend(compare_oh_my_posh(&old.oh_my_posh, &new.oh_my_posh)); diffs.extend(compare_ssh_config(&old.ssh, &new.ssh));
diffs}