modify_records/
modify_records.rs1use std::fs;
2use std::process::ExitCode;
3
4use recutils_rs::{Db, SelectionExpression};
5
6fn usage() -> ! {
7 eprintln!(
8 "usage:\n \
9 modify_records <file.rec> <Type> <expr> set <Field>=<Value>\n \
10 modify_records <file.rec> <Type> <expr> delete"
11 );
12 std::process::exit(2);
13}
14
15fn main() -> ExitCode {
16 let mut args = std::env::args().skip(1);
17 let path = args.next().unwrap_or_else(|| usage());
18 let rset_type = args.next().unwrap_or_else(|| usage());
19 let expr = args.next().unwrap_or_else(|| usage());
20 let action = args.next().unwrap_or_else(|| usage());
21
22 let text = fs::read_to_string(&path).expect("read file");
23 let mut db = Db::parse_str(&text).expect("parse");
24 let selection_expression =
25 SelectionExpression::compile(&expr, false).expect("compile selection expression");
26
27 let summary = {
28 let mut rset = db
29 .rset_by_type(&rset_type)
30 .unwrap_or_else(|| panic!("no record set of type {rset_type:?}"));
31
32 match action.as_str() {
33 "set" => {
34 let pair = args.next().unwrap_or_else(|| usage());
35 let (field, value) = pair
36 .split_once('=')
37 .unwrap_or_else(|| panic!("expected Field=Value, got {pair:?}"));
38 let mut updated = 0usize;
39 let mut missing = 0usize;
40 for mut record in rset.records().filter(|r| selection_expression.matches(r)) {
41 match record.set_field(field, value).expect("set field") {
42 true => updated += 1,
43 false => missing += 1,
44 }
45 }
46 format!(
47 "set {field}={value} on {updated} record(s); {missing} matching record(s) had no such field"
48 )
49 }
50 "delete" => {
51 let removed = rset.remove_matching(|r| selection_expression.matches(r));
52 format!("deleted {removed} record(s)")
53 }
54 other => {
55 eprintln!("unknown action {other:?}");
56 return ExitCode::from(2);
57 }
58 }
59 };
60
61 let serialized = db.to_rec_string().expect("serialize");
62 fs::write(&path, serialized).expect("write file");
63 println!("{summary}");
64 ExitCode::SUCCESS
65}