Skip to content

Commit fdd5255

Browse files
committed
Add --exit-code flag to exit with a specified value if defaults were applied.
1 parent fc26efb commit fdd5255

File tree

6 files changed

+47
-36
lines changed

6 files changed

+47
-36
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ license = "MIT"
1414
name = "macos-defaults"
1515
readme = "README.md"
1616
repository = "https://github.com/dsully/macos-defaults"
17-
version = "0.1.1"
17+
version = "0.2.0"
1818

1919
[dependencies]
2020
camino = "1.1.9"

src/cmd/apply.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ data:
7979
#[derive(Debug, Default, Serialize, Deserialize)]
8080
struct DefaultsConfig(HashMap<String, HashMap<String, plist::Value>>);
8181

82-
pub fn apply_defaults(path: &Utf8PathBuf) -> Result<()> {
82+
pub fn apply_defaults(path: &Utf8PathBuf) -> Result<bool> {
8383
//
8484
let s = fs::read_to_string(path).map_err(|e| E::FileRead {
8585
path: path.to_owned(),
@@ -129,7 +129,7 @@ pub fn apply_defaults(path: &Utf8PathBuf) -> Result<()> {
129129
}
130130

131131
if errors.is_empty() {
132-
return Ok(());
132+
return Ok(changed);
133133
}
134134

135135
for error in &errors {

src/cmd/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub mod apply;
22
pub mod dump;
33

4-
pub use apply::*;
5-
pub use dump::*;
4+
pub use apply::{apply_defaults, process_path};
5+
pub use dump::dump;

src/defaults.rs

+26-25
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
//! Utility functions for updating plist files.
22
//
33
// NB: Most of this code originated from: https://github.com/gibfahn/up-rs, MIT & Apache 2.0 licensed.
4-
//
4+
5+
use std::collections::HashMap;
6+
use std::fs::{self, File};
7+
use std::io::Read;
8+
use std::mem;
9+
510
use camino::{Utf8Path, Utf8PathBuf};
611
use color_eyre::eyre::{eyre, Result};
712
use duct::cmd;
813
use log::{debug, info, trace, warn};
914
use plist::{Dictionary, Value};
1015
use serde::{Deserialize, Serialize};
11-
use std::collections::HashMap;
12-
use std::fs::{self, File};
13-
use std::io::Read;
14-
use std::mem;
1516

1617
use super::errors::DefaultsError as E;
1718

@@ -356,12 +357,14 @@ fn merge_value(new_value: &mut Value, old_value: Option<&Value>) {
356357
/// But any duplicates between old and new values are removed, with the first value taking
357358
/// precedence.
358359
fn replace_ellipsis_array(new_value: &mut Value, old_value: Option<&Value>) {
360+
//
359361
let Value::Array(new_array) = new_value else {
360362
trace!("Value isn't an array, skipping ellipsis replacement...");
361363
return;
362364
};
363365

364366
let ellipsis = plist::Value::from(ELLIPSIS);
367+
365368
let Some(position) = new_array.iter().position(|x| x == &ellipsis) else {
366369
trace!("New value doesn't contain ellipsis, skipping ellipsis replacement...");
367370
return;
@@ -376,6 +379,7 @@ fn replace_ellipsis_array(new_value: &mut Value, old_value: Option<&Value>) {
376379
let array_copy: Vec<_> = std::mem::take(new_array);
377380

378381
trace!("Performing array ellipsis replacement...");
382+
379383
for element in array_copy {
380384
if element == ellipsis {
381385
for old_element in old_array {
@@ -390,30 +394,33 @@ fn replace_ellipsis_array(new_value: &mut Value, old_value: Option<&Value>) {
390394
}
391395
}
392396

393-
/// Recursively merge dictionaries, unless the new value is empty `{}`.
394-
/// If a dictionary
395-
/// * is empty `{}`
396-
/// * contains a key `{}`
397-
/// Then the merge step will be skipped for it and its children.
397+
// Recursively merge dictionaries, unless the new value is empty `{}`.
398+
// If a dictionary
399+
// * is empty `{}`
400+
// * contains a key `{}`
401+
// Then the merge step will be skipped for it and its children.
398402
fn deep_merge_dictionaries(new_value: &mut Value, old_value: Option<&Value>) {
403+
//
399404
let Value::Dictionary(new_dict) = new_value else {
400405
trace!("New value is not a dictionary, Skipping merge...");
401406
return;
402407
};
408+
403409
if new_dict.is_empty() {
404410
trace!("New value is an empty dictionary. Skipping merge...");
405411
return;
406412
}
413+
407414
// the "..." key is no longer used, and its merging behavior is performed by default. ignore it, for compatibility with older YAML.
408415
new_dict.remove(ELLIPSIS);
409416

410417
let Some(old_dict) = old_value.and_then(plist::Value::as_dictionary) else {
411418
trace!("Old value wasn't a dict. Skipping merge...");
412419
return;
413420
};
414-
421+
415422
// for each value, recursively invoke this to merge any child dictionaries.
416-
// also perform array ellipsis replacment.
423+
// also perform array ellipsis replacement.
417424
// this occurs even if "!" is present.
418425
for (key, new_child_value) in &mut *new_dict {
419426
let old_child_value = old_dict.get(key);
@@ -425,7 +432,9 @@ fn deep_merge_dictionaries(new_value: &mut Value, old_value: Option<&Value>) {
425432
new_dict.remove(BANG);
426433
return;
427434
}
435+
428436
trace!("Performing deep merge...");
437+
429438
for (key, old_value) in old_dict {
430439
if !new_dict.contains_key(key) {
431440
new_dict.insert(key.clone(), old_value.clone());
@@ -497,10 +506,8 @@ mod tests {
497506
use crate::defaults::deep_merge_dictionaries;
498507

499508
use super::{replace_ellipsis_array, NS_GLOBAL_DOMAIN};
500-
// use serial_test::serial;
501509

502510
#[test]
503-
// #[serial(home_dir)] // Test relies on or changes the $HOME env var.
504511
fn plist_path_tests() -> TestResult {
505512
let home_dir = dirs::home_dir().expect("Expected to be able to calculate the user's home directory.");
506513

@@ -585,7 +592,7 @@ mod tests {
585592
}
586593

587594
#[test]
588-
fn test_deep_merge_dictionaries() -> TestResult {
595+
fn test_deep_merge_dictionaries() {
589596
use plist::{Dictionary, Value};
590597

591598
let old_value = Dictionary::from_iter([
@@ -612,12 +619,10 @@ mod tests {
612619
.into();
613620

614621
assert_eq!(new_value, expected);
615-
616-
Ok(())
617622
}
618623

619624
#[test]
620-
fn test_replace_ellipsis_dict_nested() -> TestResult {
625+
fn test_replace_ellipsis_dict_nested() {
621626
use plist::{Dictionary, Value};
622627

623628
let old_value = Dictionary::from_iter([(
@@ -660,12 +665,10 @@ mod tests {
660665
.into();
661666

662667
assert_eq!(new_value, expected);
663-
664-
Ok(())
665668
}
666669

667670
#[test]
668-
fn test_replace_ellipsis_dict_nested_bang() -> TestResult {
671+
fn test_replace_ellipsis_dict_nested_bang() {
669672
use plist::{Dictionary, Value};
670673

671674
let old_value = Dictionary::from_iter([(
@@ -702,12 +705,10 @@ mod tests {
702705
.into();
703706

704707
assert_eq!(new_value, expected);
705-
706-
Ok(())
707708
}
708709

709710
#[test]
710-
fn test_replace_ellipsis_array() -> TestResult {
711+
fn test_replace_ellipsis_array() {
711712
let old_value = vec![
712713
10.into(), // !
713714
20.into(), // !
@@ -736,7 +737,7 @@ mod tests {
736737
50.into(),
737738
]
738739
.into();
740+
739741
assert_eq!(new_value, expected);
740-
Ok(())
741742
}
742743
}

src/main.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ pub(crate) enum Commands {
6262
/// Sets the input file or path to use.
6363
#[arg(required = true, value_hint = ValueHint::FilePath)]
6464
path: Utf8PathBuf,
65+
66+
/// If changes were applied, exit with this return code.
67+
#[clap(short, long, default_value = "0")]
68+
exit_code: i32,
6569
},
6670

6771
/// Generate shell completions to stdout.
@@ -103,18 +107,22 @@ fn main() -> Result<()> {
103107
env_logger::Builder::new().filter_level(cli.verbose.log_level_filter()).init();
104108

105109
match cli.command {
106-
Commands::Apply { path } => {
110+
Commands::Apply { path, exit_code } => {
111+
//
112+
let mut changed = false;
113+
107114
for p in process_path(path)? {
108115
fs::metadata(&p).map_err(|e| E::FileRead { path: p.clone(), source: e })?;
109116

110-
apply_defaults(&p)?;
117+
if apply_defaults(&p)? {
118+
changed = true;
119+
}
111120
}
112121

113-
Ok(())
122+
std::process::exit(if changed { exit_code } else { 0 });
114123
}
115124
Commands::Completions { shell } => {
116125
generate(shell, &mut CLI::command(), "macos-defaults", &mut io::stdout().lock());
117-
118126
Ok(())
119127
}
120128
Commands::Dump {
@@ -123,5 +131,7 @@ fn main() -> Result<()> {
123131
global_domain,
124132
domain,
125133
} => dump(current_host, path, global_domain, domain),
126-
}
134+
}?;
135+
136+
std::process::exit(0);
127137
}

0 commit comments

Comments
 (0)