Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_file/examples/edit_office.rs

3.5 KiB, 5 runs

created by r1870400018:22953, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Edits a document or a spreadsheet that already exists, so an external reader can be asked whether
2//! the result is still a file it recognises.
3//!
4//! ```text
5//! cargo run -p oxedyne_fe2o3_file --example edit_office -- in.docx out.docx "old=>new"
6//! cargo run -p oxedyne_fe2o3_file --example edit_office -- in.ods out.ods "Sheet1!B7=3.5"
7//! cargo run -p oxedyne_fe2o3_file --example edit_office -- in.xlsx out.xlsx "!D2:=B2*C2"
8//! ```
9//!
10//! A find-and-replace is `find=>replace`. A cell is `[sheet]!ref=value`, or `[sheet]!ref:=formula`,
11//! and the sheet may be left empty for the first one.
12//!
13//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
14//! Anthropic Claude
15
16use oxedyne_fe2o3_core::prelude::*;
17use oxedyne_fe2o3_file::office::docx;
18use oxedyne_fe2o3_file::office::edit::Find;
19use oxedyne_fe2o3_file::office::odf;
20use oxedyne_fe2o3_file::office::sheet::Ref;
21use oxedyne_fe2o3_file::office::xlsx;
22
23fn main() -> Outcome<()> {
24 let args: Vec<String> = std::env::args().collect();
25 if args.len() < 4 {
26 return Err(err!("Usage: edit_office <in> <out> <edit> [edit ...]"; Invalid, Input));
27 }
28 let src = res!(std::fs::read(&args[1]), IO, File);
29 let asks = &args[3..];
30 let out = match args[1].rsplit('.').next().unwrap_or("") {
31 "docx" => {
32 let e = res!(docx::edit::edit(&src, &res!(finds(asks))));
33 println!("{} runs rewritten", e.runs);
34 e.bytes
35 }
36 "odt" => {
37 let e = res!(odf::text::edit(&src, &res!(finds(asks))));
38 println!("{} runs rewritten", e.runs);
39 e.bytes
40 }
41 "xlsx" => {
42 let sets = res!(cells(asks));
43 let sets: Vec<xlsx::edit::Set> = sets.into_iter()
44 .map(|(sheet, at, value, formula)| xlsx::edit::Set { sheet, at, value, formula })
45 .collect();
46 let e = res!(xlsx::edit::edit(&src, &sets));
47 println!("{} cells written to {}", e.cells, e.sheets.join(", "));
48 e.bytes
49 }
50 "ods" => {
51 let sets = res!(cells(asks));
52 let sets: Vec<odf::sheet::Set> = sets.into_iter()
53 .map(|(sheet, at, value, formula)| odf::sheet::Set { sheet, at, value, formula })
54 .collect();
55 let e = res!(odf::sheet::edit(&src, &sets));
56 println!("{} cells written to {}", e.cells, e.sheets.join(", "));
57 e.bytes
58 }
59 other => return Err(err!("'{}' is not a format this edits.", other; Invalid, Input)),
60 };
61 res!(std::fs::write(&args[2], &out), IO, File);
62 println!("{}: {} bytes", args[2], out.len());
63 Ok(())
64}
65
66/// The find-and-replace edits a `find=>replace` argument asks for.
67fn finds(asks: &[String]) -> Outcome<Vec<Find>> {
68 let mut out = Vec::new();
69 for a in asks {
70 let (find, replace) = res!(a.split_once("=>").ok_or_else(|| err!(
71 "'{}' is not an edit. One looks like `old=>new`.", a; Invalid, Input)));
72 out.push(Find::every(find, replace));
73 }
74 Ok(out)
75}
76
77/// The cells a `[sheet]!ref=value` argument asks for.
78fn cells(asks: &[String]) -> Outcome<Vec<(Option<String>, Ref, Option<String>, Option<String>)>> {
79 let mut out = Vec::new();
80 for a in asks {
81 let (sheet, rest) = res!(a.split_once('!').ok_or_else(|| err!(
82 "'{}' is not a cell. One looks like `Sheet1!B2=3.5`.", a; Invalid, Input)));
83 let sheet = match sheet.is_empty() {
84 true => None,
85 false => Some(sheet.to_string()),
86 };
87 match rest.split_once(":=") {
88 Some((at, f)) => out.push((sheet, res!(Ref::parse(at)), None, Some(f.to_string()))),
89 None => {
90 let (at, v) = res!(rest.split_once('=').ok_or_else(|| err!(
91 "'{}' names no value. One looks like `Sheet1!B2=3.5`.", a; Invalid, Input)));
92 out.push((sheet, res!(Ref::parse(at)), Some(v.to_string()), None));
93 }
94 }
95 }
96 Ok(out)
97}