| 1 | #[path = "../src/flush.rs"] |
| 2 | mod flush; |
| 3 | #[path = "support/typing.rs"] |
| 4 | mod typing; |
| 5 | |
| 6 | use onestore::{ |
| 7 | document::{Format, Layout}, |
| 8 | op::{Edit, Op, PageOp}, |
| 9 | page::{ |
| 10 | Outline, PageObject, PageParagraph, Paragraph, ParagraphContent, TextObject, text::new_id, |
| 11 | }, |
| 12 | }; |
| 13 | use std::{fs, io::Write}; |
| 14 | |
| 15 | /// A paragraph holding `text` in OneNote's default text style. |
| 16 | fn paragraph(text: &str) -> Result<PageParagraph, Box<dyn std::error::Error>> { |
| 17 | let format = Format { |
| 18 | font: Some("Calibri".into()), |
| 19 | font_size: Some(11.0), |
| 20 | language: Some(0x409), |
| 21 | ..Format::default() |
| 22 | }; |
| 23 | Ok(PageParagraph { |
| 24 | id: new_id()?, |
| 25 | parent: None, |
| 26 | level: 1, |
| 27 | style: None, |
| 28 | format: Format::default(), |
| 29 | content: ParagraphContent::Text(TextObject { |
| 30 | id: new_id()?, |
| 31 | date_field: None, |
| 32 | text: Paragraph::new(text.into(), format), |
| 33 | tags: Vec::new(), |
| 34 | }), |
| 35 | lists: Vec::new(), |
| 36 | tags: Vec::new(), |
| 37 | media: Default::default(), |
| 38 | collapsed: false, |
| 39 | }) |
| 40 | } |
| 41 | |
| 42 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 43 | let args: Vec<_> = std::env::args().skip(1).collect(); |
| 44 | let usage = "Usage: insert paragraph INPUT OUTPUT|--in-place SPACE CONTAINER BEFORE|- TEXT AUTHOR\n insert outline INPUT OUTPUT|--in-place SPACE X Y TEXT AUTHOR"; |
| 45 | let (op, object, text, author) = match args.first().map(String::as_str) { |
| 46 | Some("paragraph") if args.len() == 8 => { |
| 47 | let paragraph = paragraph(&args[6])?; |
| 48 | let (object, text) = (paragraph.id, paragraph.text().unwrap().id); |
| 49 | let op = PageOp::Insert { |
| 50 | container: args[4].parse()?, |
| 51 | before: if args[5] == "-" { |
| 52 | None |
| 53 | } else { |
| 54 | Some(args[5].parse()?) |
| 55 | }, |
| 56 | paragraphs: vec![paragraph], |
| 57 | }; |
| 58 | (op, object, text, &args[7]) |
| 59 | } |
| 60 | Some("outline") if args.len() == 8 => { |
| 61 | let paragraph = paragraph(&args[6])?; |
| 62 | let text = paragraph.text().unwrap().id; |
| 63 | let object = new_id()?; |
| 64 | let op = PageOp::Add { |
| 65 | object: PageObject::Outline(Outline { |
| 66 | id: object, |
| 67 | title: false, |
| 68 | min_width: None, |
| 69 | layout: Layout { |
| 70 | x: Some(args[4].parse()?), |
| 71 | y: Some(args[5].parse()?), |
| 72 | ..Layout::default() |
| 73 | }, |
| 74 | indents: Vec::new(), |
| 75 | paragraphs: vec![paragraph], |
| 76 | unsupported: Vec::new(), |
| 77 | }), |
| 78 | before: None, |
| 79 | }; |
| 80 | (op, object, text, &args[7]) |
| 81 | } |
| 82 | _ => return Err(usage.into()), |
| 83 | }; |
| 84 | let source = onestore::read_file(&args[1])?; |
| 85 | let edit = Edit { |
| 86 | at: typing::now(), |
| 87 | ops: vec![Op::Page { |
| 88 | space: args[3].parse()?, |
| 89 | op, |
| 90 | }], |
| 91 | }; |
| 92 | let transaction = |
| 93 | typing::sealed(&source, author, &edit)?.ok_or("The insertion stores nothing")?; |
| 94 | let mut record = serde_json::json!({"edit": edit, "object": object, "text_object": text}); |
| 95 | if args[2] == "--in-place" { |
| 96 | if let Err(error) = transaction.commit_file(&args[1]) { |
| 97 | record["state"] = format!("{:?}", error.state).into(); |
| 98 | record["error"] = error.error.to_string().into(); |
| 99 | println!("{record}"); |
| 100 | std::process::exit(2); |
| 101 | } |
| 102 | record["state"] = "Committed".into(); |
| 103 | } else { |
| 104 | let mut written = source; |
| 105 | transaction.apply(&mut written)?; |
| 106 | let mut file = fs::OpenOptions::new() |
| 107 | .write(true) |
| 108 | .create_new(true) |
| 109 | .open(&args[2])?; |
| 110 | file.write_all(&written)?; |
| 111 | flush::flush(&file)?; |
| 112 | record["state"] = "Created".into(); |
| 113 | } |
| 114 | println!("{record}"); |
| 115 | Ok(()) |
| 116 | } |