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 | |
| 16 | use oxedyne_fe2o3_core::prelude::*; |
| 17 | use oxedyne_fe2o3_file::office::docx; |
| 18 | use oxedyne_fe2o3_file::office::edit::Find; |
| 19 | use oxedyne_fe2o3_file::office::odf; |
| 20 | use oxedyne_fe2o3_file::office::sheet::Ref; |
| 21 | use oxedyne_fe2o3_file::office::xlsx; |
| 22 | |
| 23 | fn 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. |
| 67 | fn 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. |
| 78 | fn 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 | } |