From ca10ca277b1421d75ecdf3a504ca4593dcadd2f6 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 12 Feb 2023 00:54:09 +0100 Subject: [PATCH] Add option to export to stdout --- CHANGELOG.md | 1 + src/export.rs | 45 ++++++++++++++++++++++++++++++++------------- src/export/json.rs | 15 +++------------ src/export/text.rs | 26 +++++++++++--------------- 4 files changed, 47 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e4dcb..10bf4a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Procedure when bumping the version number: ### Added - `--verbose` flag - `json-stream` room export format +- Option to export to stdout ### Changed - Respect colon-delimited emoji when calculating nick hue diff --git a/src/export.rs b/src/export.rs index a2831a4..15db9b7 100644 --- a/src/export.rs +++ b/src/export.rs @@ -4,9 +4,9 @@ mod json; mod text; use std::fs::File; -use std::io::{BufWriter, Write}; +use std::io::{self, BufWriter, Write}; -use crate::vault::EuphVault; +use crate::vault::{EuphRoomVault, EuphVault}; #[derive(Debug, Clone, Copy, clap::ValueEnum)] pub enum Format { @@ -57,12 +57,28 @@ pub struct Args { /// If the value ends with a `/`, it is assumed to point to a directory and /// `%r.%e` will be appended. /// + /// If the value is a literal `-`, the export will be written to stdout. To + /// write to a file named `-`, you can use `./-`. + /// /// Must be a valid utf-8 encoded string. #[arg(long, short, default_value_t = Into::into("%r.%e"))] #[arg(verbatim_doc_comment)] out: String, } +async fn export_room( + vault: &EuphRoomVault, + out: &mut W, + format: Format, +) -> anyhow::Result<()> { + match format { + Format::Text => text::export(vault, out).await?, + Format::Json => json::export(vault, out).await?, + Format::JsonStream => json::export_stream(vault, out).await?, + } + Ok(()) +} + pub async fn export(vault: &EuphVault, mut args: Args) -> anyhow::Result<()> { if args.out.ends_with('/') { args.out.push_str("%r.%e"); @@ -79,21 +95,24 @@ pub async fn export(vault: &EuphVault, mut args: Args) -> anyhow::Result<()> { }; if rooms.is_empty() { - println!("No rooms to export"); + eprintln!("No rooms to export"); } for room in rooms { - let out = format_out(&args.out, &room, args.format); - println!("Exporting &{room} as {} to {out}", args.format.name()); - - let vault = vault.room(room); - let mut file = BufWriter::new(File::create(out)?); - match args.format { - Format::Text => text::export_to_file(&vault, &mut file).await?, - Format::Json => json::export_to_file(&vault, &mut file).await?, - Format::JsonStream => json::export_stream_to_file(&vault, &mut file).await?, + if args.out == "-" { + eprintln!("Exporting &{room} as {} to stdout", args.format.name()); + let vault = vault.room(room); + let mut stdout = BufWriter::new(io::stdout()); + export_room(&vault, &mut stdout, args.format).await?; + stdout.flush()?; + } else { + let out = format_out(&args.out, &room, args.format); + eprintln!("Exporting &{room} as {} to {out}", args.format.name()); + let vault = vault.room(room); + let mut file = BufWriter::new(File::create(out)?); + export_room(&vault, &mut file, args.format).await?; + file.flush()?; } - file.flush()?; } Ok(()) diff --git a/src/export/json.rs b/src/export/json.rs index 998087e..6fb3d13 100644 --- a/src/export/json.rs +++ b/src/export/json.rs @@ -1,14 +1,10 @@ -use std::fs::File; -use std::io::{BufWriter, Write}; +use std::io::Write; use crate::vault::EuphRoomVault; const CHUNK_SIZE: usize = 10000; -pub async fn export_to_file( - vault: &EuphRoomVault, - file: &mut BufWriter, -) -> anyhow::Result<()> { +pub async fn export(vault: &EuphRoomVault, file: &mut W) -> anyhow::Result<()> { write!(file, "[")?; let mut total = 0; @@ -39,14 +35,10 @@ pub async fn export_to_file( write!(file, "\n]")?; println!(" {total} messages in total"); - Ok(()) } -pub async fn export_stream_to_file( - vault: &EuphRoomVault, - file: &mut BufWriter, -) -> anyhow::Result<()> { +pub async fn export_stream(vault: &EuphRoomVault, file: &mut W) -> anyhow::Result<()> { let mut total = 0; let mut offset = 0; loop { @@ -69,6 +61,5 @@ pub async fn export_stream_to_file( } println!(" {total} messages in total"); - Ok(()) } diff --git a/src/export/text.rs b/src/export/text.rs index b4b55db..9dd7c6b 100644 --- a/src/export/text.rs +++ b/src/export/text.rs @@ -1,5 +1,4 @@ -use std::fs::File; -use std::io::{BufWriter, Write}; +use std::io::Write; use euphoxide::api::MessageId; use time::format_description::FormatItem; @@ -14,16 +13,13 @@ const TIME_FORMAT: &[FormatItem<'_>] = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); const TIME_EMPTY: &str = " "; -pub async fn export_to_file( - vault: &EuphRoomVault, - file: &mut BufWriter, -) -> anyhow::Result<()> { +pub async fn export(vault: &EuphRoomVault, out: &mut W) -> anyhow::Result<()> { let mut exported_trees = 0; let mut exported_msgs = 0; let mut root_id = vault.first_root_id().await; while let Some(some_root_id) = root_id { let tree = vault.tree(some_root_id).await; - write_tree(file, &tree, some_root_id, 0)?; + write_tree(out, &tree, some_root_id, 0)?; root_id = vault.next_root_id(some_root_id).await; exported_trees += 1; @@ -38,8 +34,8 @@ pub async fn export_to_file( Ok(()) } -fn write_tree( - file: &mut BufWriter, +fn write_tree( + out: &mut W, tree: &Tree, id: MessageId, indent: usize, @@ -47,22 +43,22 @@ fn write_tree( let indent_string = "| ".repeat(indent); if let Some(msg) = tree.msg(&id) { - write_msg(file, &indent_string, msg)?; + write_msg(out, &indent_string, msg)?; } else { - write_placeholder(file, &indent_string)?; + write_placeholder(out, &indent_string)?; } if let Some(children) = tree.children(&id) { for child in children { - write_tree(file, tree, *child, indent + 1)?; + write_tree(out, tree, *child, indent + 1)?; } } Ok(()) } -fn write_msg( - file: &mut BufWriter, +fn write_msg( + file: &mut W, indent_string: &str, msg: &SmallMessage, ) -> anyhow::Result<()> { @@ -85,7 +81,7 @@ fn write_msg( Ok(()) } -fn write_placeholder(file: &mut BufWriter, indent_string: &str) -> anyhow::Result<()> { +fn write_placeholder(file: &mut W, indent_string: &str) -> anyhow::Result<()> { writeln!(file, "{TIME_EMPTY} {indent_string}[...]")?; Ok(()) }