Apply block quote to multiline content
This commit is contained in:
parent
4dae4360cc
commit
b861469319
4 changed files with 87 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -932,6 +932,7 @@ dependencies = [
|
|||
"html2md",
|
||||
"log",
|
||||
"megalodon",
|
||||
"regex",
|
||||
"rss",
|
||||
"rustyline",
|
||||
"tokio",
|
||||
|
|
|
@ -13,6 +13,7 @@ env_logger = "0.10.0"
|
|||
html2md = "0.2.14"
|
||||
log = "0.4.19"
|
||||
megalodon = "0.8.3"
|
||||
regex = "1.9.1"
|
||||
rss = "2.0.4"
|
||||
rustyline = "12.0.0"
|
||||
tokio = { version = "1.28.2", features = ["default", "full"] }
|
||||
|
|
|
@ -6,6 +6,7 @@ use megalodon::{
|
|||
response::Response,
|
||||
Megalodon,
|
||||
};
|
||||
use regex::{Captures, Regex};
|
||||
use rustyline::DefaultEditor;
|
||||
use url::Url;
|
||||
use url_open::UrlOpen;
|
||||
|
@ -19,7 +20,7 @@ pub(super) async fn format_status(
|
|||
"{}
|
||||
> {}{}",
|
||||
status.created_at.with_timezone(&Local).format("%H:%M"),
|
||||
parse_html(&status.content).trim(),
|
||||
apply_block_quote(1, (&status.content).trim()),
|
||||
format_attachments(1, &status.media_attachments)
|
||||
);
|
||||
let Response { json, .. } = client.get_status_context(status.id.clone(), None).await?;
|
||||
|
@ -35,7 +36,7 @@ pub(super) async fn format_status(
|
|||
> {}
|
||||
>> {}{}",
|
||||
status.created_at.with_timezone(&Local).format("%H:%M"),
|
||||
parse_html(&status.content).trim(),
|
||||
apply_block_quote(2, parse_html(&status.content).trim()),
|
||||
format_attachments(2, &status.media_attachments)
|
||||
)
|
||||
})
|
||||
|
@ -52,8 +53,11 @@ pub(super) async fn format_status(
|
|||
))
|
||||
}
|
||||
|
||||
fn quote_prefix(depth: usize) -> String {
|
||||
vec![">"; depth].join("")
|
||||
}
|
||||
|
||||
fn format_attachments(depth: usize, attachments: &[Attachment]) -> String {
|
||||
let prefix = vec![">"; depth].join("");
|
||||
if attachments.is_empty() {
|
||||
String::default()
|
||||
} else {
|
||||
|
@ -69,10 +73,76 @@ fn format_attachments(depth: usize, attachments: &[Attachment]) -> String {
|
|||
} else {
|
||||
a.url.clone()
|
||||
};
|
||||
format!("{} <img src=\"{}\" />", prefix, src)
|
||||
format!("{} <img src=\"{}\" />", quote_prefix(depth), src)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// without processing the content per line, the block quote markdown is only added to the first
|
||||
// line, breaking the desired formatting
|
||||
fn apply_block_quote<S: AsRef<str>>(depth: usize, content: S) -> String {
|
||||
let prefix = quote_prefix(depth);
|
||||
let empty = Regex::new("\n\\s").expect("Failed to compiled regex for nested empty lines!");
|
||||
let non_empty =
|
||||
Regex::new("\n(?P<initial>[^>\\s])").expect("Failed to compile regex for nested lines!");
|
||||
// parsing some multiline content adds paragraph tags, convert those back to new lines to apply
|
||||
// the block quote level to each line
|
||||
let content = content.as_ref().replace("</p><p>", "\n\n");
|
||||
let content = content.replace("<p>", "");
|
||||
let content = content.replace("</p>", "");
|
||||
// replace separately to avoid trailing spaces when replacing empty lines with the prefix
|
||||
let content = empty.replace(content.as_ref(), format!("\n{}\n", prefix));
|
||||
let content = non_empty.replace_all(&content, |c: &Captures| {
|
||||
format!("\n{} {}", prefix, c["initial"].to_string())
|
||||
});
|
||||
content.to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::format::apply_block_quote;
|
||||
|
||||
#[test]
|
||||
fn test_apply_bq() {
|
||||
assert_eq!(
|
||||
"Foo
|
||||
>
|
||||
> Bar"
|
||||
.to_owned(),
|
||||
apply_block_quote(
|
||||
1, "Foo
|
||||
|
||||
Bar"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_bq_deeper() {
|
||||
assert_eq!(
|
||||
"Foo
|
||||
>>
|
||||
>> Bar"
|
||||
.to_owned(),
|
||||
apply_block_quote(
|
||||
2, "Foo
|
||||
|
||||
Bar"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_bq_strip_ps() {
|
||||
assert_eq!(
|
||||
"Foo
|
||||
>>
|
||||
>> Bar"
|
||||
.to_owned(),
|
||||
apply_block_quote(2, "<p>Foo</p><p>Bar</p>")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -27,12 +27,12 @@ mod range;
|
|||
#[derive(Debug, Parser)]
|
||||
#[command()]
|
||||
struct Config {
|
||||
#[arg(short, long, env = "MASTODON_URL")]
|
||||
#[arg(short, long, env = "MASTODON_URL", required = true)]
|
||||
url: String,
|
||||
#[arg(short, long, env = "MASTODON_ACCESS_TOKEN")]
|
||||
#[arg(short, long, env = "MASTODON_ACCESS_TOKEN", required = true)]
|
||||
access_token: String,
|
||||
#[arg(short, long)]
|
||||
output_dir: String,
|
||||
output_dir: Option<String>,
|
||||
#[arg(required = true)]
|
||||
date: String,
|
||||
#[arg(short, long)]
|
||||
|
@ -108,10 +108,14 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
reversed.reverse();
|
||||
let output = format!("{}{}.md", output_dir, date);
|
||||
let mut f = File::options().append(true).open(&output)?;
|
||||
f.write_all(&reversed.join("\n\n").as_bytes())?;
|
||||
println!("Appended matching posts to {}.", output);
|
||||
if let Some(output_dir) = output_dir {
|
||||
let output = format!("{}{}.md", output_dir, date);
|
||||
let mut f = File::options().append(true).open(&output)?;
|
||||
f.write_all(&reversed.join("\n\n").as_bytes())?;
|
||||
println!("Appended matching posts to {}.", output);
|
||||
} else {
|
||||
println!("{}", reversed.join("\n\n"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue