Skip to main content

modify_records/
modify_records.rs

1use 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}