From 4a7a2548ae16f973aca989a7747a624510a1b64f Mon Sep 17 00:00:00 2001 From: Thaumy Date: Thu, 21 Sep 2023 11:34:35 +0800 Subject: [PATCH] feat!: time style option --- src/args/mod.rs | 18 +++++++++++++ src/args/parser.rs | 16 ++++++++++++ src/display/colorful.rs | 58 ++++++++++++----------------------------- src/display/mod.rs | 35 ++++++++++++++++--------- src/display/normal.rs | 58 ++++++++++++----------------------------- src/infra/time.rs | 19 +++++++++++--- src/main.rs | 11 ++++---- 7 files changed, 111 insertions(+), 104 deletions(-) diff --git a/src/args/mod.rs b/src/args/mod.rs index fe202e4..c344055 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -11,6 +11,12 @@ pub enum Style { Json, } +#[derive(Clone, Debug, Parser, ValueEnum)] +pub enum TimeStyle { + Friendly, + Normal, +} + #[derive(Debug, Parser)] #[command(author, about, long_about = None, version)] pub struct Args { @@ -81,6 +87,18 @@ pub struct Args { #[arg(value_name = "NAME")] pub style: Style, + #[arg(verbatim_doc_comment)] + /// Configure the time style + /// Example: cnb --style normal ing list + /// This option does not affect the output of '--style json' + /// * + #[arg(long)] + #[arg(value_enum)] + #[arg(hide_possible_values = true)] + #[arg(default_value_t = TimeStyle::Friendly)] + #[arg(value_name = "NAME")] + pub time_style: TimeStyle, + #[arg(verbatim_doc_comment)] /// Fail if error occurred /// Example: cnb --fail-on-error ing list diff --git a/src/args/parser.rs b/src/args/parser.rs index d4c7e73..cb4ceee 100644 --- a/src/args/parser.rs +++ b/src/args/parser.rs @@ -22,6 +22,7 @@ pub const fn no_operation(args: &Args) -> bool { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } @@ -44,6 +45,7 @@ pub const fn user_info(args: &Args) -> bool { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } @@ -66,6 +68,7 @@ pub fn publish_ing(args: &Args) -> Option<&String> { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => content, @@ -90,6 +93,7 @@ pub fn login(args: &Args) -> Option<&String> { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => pat, @@ -114,6 +118,7 @@ pub const fn logout(args: &Args) -> bool { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } @@ -136,6 +141,7 @@ pub fn list_ing(args: &Args) -> Option<(usize, usize, IngType, bool)> { take, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => { @@ -165,6 +171,7 @@ pub fn comment_ing(args: &Args) -> Option<(&String, usize)> { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => (content, *id), @@ -193,6 +200,7 @@ pub fn show_post(args: &Args) -> Option { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => *id, @@ -221,6 +229,7 @@ pub fn show_post_meta(args: &Args) -> Option { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => *id, @@ -249,6 +258,7 @@ pub fn show_post_comment(args: &Args) -> Option { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => *id, @@ -277,6 +287,7 @@ pub fn list_post(args: &Args) -> Option<(usize, usize)> { take, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => { @@ -309,6 +320,7 @@ pub fn delete_post(args: &Args) -> Option { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => *id, @@ -337,6 +349,7 @@ pub fn search_post(args: &Args) -> Option<(&String, usize, usize)> { take, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => { @@ -374,6 +387,7 @@ pub fn create_post(args: &Args) -> Option<(&String, &String, bool)> { take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => (title, body, *publish), @@ -411,6 +425,7 @@ pub fn update_post( take: None, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => (*id, title, body, publish), @@ -430,6 +445,7 @@ pub fn list_news(args: &Args) -> Option<(usize, usize)> { take, debug: _, style: _, + time_style: _, fail_on_error: _, quiet: _, } => { diff --git a/src/display/colorful.rs b/src/display/colorful.rs index bbdcd75..a2e1305 100644 --- a/src/display/colorful.rs +++ b/src/display/colorful.rs @@ -7,11 +7,11 @@ use crate::api::news::get_list::NewsEntry; use crate::api::post::get_comment_list::PostCommentEntry; use crate::api::post::get_one::PostEntry; use crate::api::user::info::UserInfo; +use crate::args::TimeStyle; use crate::infra::iter::IteratorExt; use crate::infra::str::StrExt; -use crate::infra::time::{fmt_time_to_string_friendly, patch_rfc3339}; +use crate::infra::time::display_cnb_time; use anyhow::Result; -use chrono::{DateTime, Local, Utc}; use colored::Colorize; use std::fmt::Display; use std::ops::Not; @@ -54,6 +54,7 @@ pub fn user_info(info: &Result) { } pub fn list_ing( + time_style: &TimeStyle, ing_with_comment_list: &Result)>>, rev: bool, align: bool, @@ -67,15 +68,9 @@ pub fn list_ing( .iter() .dyn_rev(rev) .for_each(|(ing, comment_list)| { - let create_time = { - let rfc3339 = patch_rfc3339(&ing.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; - + let create_time = display_cnb_time(&ing.create_time, time_style); print!("{}", create_time.dimmed()); + let send_from_mark = match ing.send_from { IngSendFrom::Cli => Some("CLI"), IngSendFrom::CellPhone => Some("Mobile"), @@ -154,7 +149,7 @@ pub fn show_post(entry: &Result) { } } -pub fn show_post_meta(entry: &Result) { +pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) { let entry = match entry { Ok(entry) => entry, Err(e) => return println_err(e), @@ -186,39 +181,25 @@ pub fn show_post_meta(entry: &Result) { println!("Tags {}", tags_text); } } - let create_time = { - let rfc3339 = patch_rfc3339(&entry.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let create_time = display_cnb_time(&entry.create_time, time_style); println!("Create {}", create_time); - let modify_time = { - let rfc3339 = patch_rfc3339(&entry.modify_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let modify_time = display_cnb_time(&entry.create_time, time_style); println!("Modify {}", modify_time); println!("Link https:{}", entry.url); } -pub fn show_post_comment(comment_list: &Result>, rev: bool) { +pub fn show_post_comment( + time_style: &TimeStyle, + comment_list: &Result>, + rev: bool, +) { let comment_list = match comment_list { Ok(entry) => entry, Err(e) => return println_err(e), }; comment_list.iter().dyn_rev(rev).for_each(|comment| { - let create_time = { - let rfc3339 = patch_rfc3339(&comment.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let create_time = display_cnb_time(&comment.create_time, time_style); let floor_text = format!("{}F", comment.floor); println!("{} {}", create_time.dimmed(), floor_text.dimmed()); println!(" {} {}", comment.user_name.cyan(), comment.content); @@ -271,21 +252,14 @@ pub fn println_result(result: &Result) { } } -pub fn list_news(news_list: &Result>, rev: bool) { +pub fn list_news(time_style: &TimeStyle, news_list: &Result>, rev: bool) { let news_list = match news_list { Ok(o) => o, Err(e) => return println_err(e), }; news_list.iter().dyn_rev(rev).for_each(|news| { - let create_time = { - let rfc3339 = patch_rfc3339(&news.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; - + let create_time = display_cnb_time(&news.create_time, time_style); let url = format!("https://news.cnblogs.com/n/{}", news.id); println!("{} {}", create_time.dimmed(), url.dimmed(),); println!(" {}", news.title); diff --git a/src/display/mod.rs b/src/display/mod.rs index 194ec45..6c365d8 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -4,7 +4,7 @@ use crate::api::news::get_list::NewsEntry; use crate::api::post::get_comment_list::PostCommentEntry; use crate::api::post::get_one::PostEntry; use crate::api::user::info::UserInfo; -use crate::args::Style; +use crate::args::{Style, TimeStyle}; use anyhow::Result; use std::path::PathBuf; @@ -38,13 +38,14 @@ pub fn user_info(style: &Style, user_info: &Result) { pub fn list_ing( style: &Style, + time_style: &TimeStyle, ing_with_comment_list: &Result)>>, rev: bool, align: bool, ) { match style { - Style::Colorful => colorful::list_ing(ing_with_comment_list, rev, align), - Style::Normal => normal::list_ing(ing_with_comment_list, rev, align), + Style::Colorful => colorful::list_ing(time_style, ing_with_comment_list, rev, align), + Style::Normal => normal::list_ing(time_style, ing_with_comment_list, rev, align), Style::Json => json::list_ing(ing_with_comment_list, rev), } } @@ -73,18 +74,23 @@ pub fn show_post(style: &Style, entry: &Result) { } } -pub fn show_post_meta(style: &Style, entry: &Result) { +pub fn show_post_meta(style: &Style, time_style: &TimeStyle, entry: &Result) { match style { - Style::Colorful => colorful::show_post_meta(entry), - Style::Normal => normal::show_post_meta(entry), + Style::Colorful => colorful::show_post_meta(time_style, entry), + Style::Normal => normal::show_post_meta(time_style, entry), Style::Json => json::show_post_meta(entry), } } -pub fn show_post_comment(style: &Style, comment_list: &Result>, rev: bool) { +pub fn show_post_comment( + style: &Style, + time_style: &TimeStyle, + comment_list: &Result>, + rev: bool, +) { match style { - Style::Colorful => colorful::show_post_comment(comment_list, rev), - Style::Normal => normal::show_post_comment(comment_list, rev), + Style::Colorful => colorful::show_post_comment(time_style, comment_list, rev), + Style::Normal => normal::show_post_comment(time_style, comment_list, rev), Style::Json => json::show_post_comment(comment_list, rev), } } @@ -129,10 +135,15 @@ pub fn update_post(style: &Style, result: &Result) { } } -pub fn list_news(style: &Style, news_list: &Result>, rev: bool) { +pub fn list_news( + style: &Style, + time_style: &TimeStyle, + news_list: &Result>, + rev: bool, +) { match style { - Style::Colorful => colorful::list_news(news_list, rev), - Style::Normal => normal::list_news(news_list, rev), + Style::Colorful => colorful::list_news(time_style, news_list, rev), + Style::Normal => normal::list_news(time_style, news_list, rev), Style::Json => json::list_news(news_list, rev), } } diff --git a/src/display/normal.rs b/src/display/normal.rs index efca02e..3042e06 100644 --- a/src/display/normal.rs +++ b/src/display/normal.rs @@ -7,11 +7,11 @@ use crate::api::news::get_list::NewsEntry; use crate::api::post::get_comment_list::PostCommentEntry; use crate::api::post::get_one::PostEntry; use crate::api::user::info::UserInfo; +use crate::args::TimeStyle; use crate::infra::iter::IteratorExt; use crate::infra::str::StrExt; -use crate::infra::time::{fmt_time_to_string_friendly, patch_rfc3339}; +use crate::infra::time::display_cnb_time; use anyhow::Result; -use chrono::{DateTime, Local, Utc}; use colored::Colorize; use std::fmt::Display; use std::ops::Not; @@ -54,6 +54,7 @@ pub fn user_info(info: &Result) { } pub fn list_ing( + time_style: &TimeStyle, ing_with_comment_list: &Result)>>, rev: bool, align: bool, @@ -67,15 +68,9 @@ pub fn list_ing( .iter() .dyn_rev(rev) .for_each(|(ing, comment_list)| { - let create_time = { - let rfc3339 = patch_rfc3339(&ing.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; - + let create_time = display_cnb_time(&ing.create_time, time_style); print!("{}", create_time); + let send_from_mark = match ing.send_from { IngSendFrom::Cli => Some("CLI"), IngSendFrom::CellPhone => Some("Mobile"), @@ -154,7 +149,7 @@ pub fn show_post(entry: &Result) { } } -pub fn show_post_meta(entry: &Result) { +pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) { let entry = match entry { Ok(o) => o, Err(e) => return println_err(e), @@ -186,39 +181,25 @@ pub fn show_post_meta(entry: &Result) { println!("Tags {}", tags_text); } } - let create_time = { - let rfc3339 = patch_rfc3339(&entry.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let create_time = display_cnb_time(&entry.create_time, time_style); println!("Create {}", create_time); - let modify_time = { - let rfc3339 = patch_rfc3339(&entry.modify_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let modify_time = display_cnb_time(&entry.modify_time, time_style); println!("Modify {}", modify_time); println!("Link https:{}", entry.url); } -pub fn show_post_comment(comment_list: &Result>, rev: bool) { +pub fn show_post_comment( + time_style: &TimeStyle, + comment_list: &Result>, + rev: bool, +) { let comment_list = match comment_list { Ok(entry) => entry, Err(e) => return println_err(e), }; comment_list.iter().dyn_rev(rev).for_each(|comment| { - let create_time = { - let rfc3339 = patch_rfc3339(&comment.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; + let create_time = display_cnb_time(&comment.create_time, time_style); println!("{} {}F", create_time, comment.floor); println!(" {} {}", comment.user_name, comment.content); }) @@ -270,21 +251,14 @@ pub fn println_result(result: &Result) { } } -pub fn list_news(news_list: &Result>, rev: bool) { +pub fn list_news(time_style: &TimeStyle, news_list: &Result>, rev: bool) { let news_list = match news_list { Ok(o) => o, Err(e) => return println_err(e), }; news_list.iter().dyn_rev(rev).for_each(|news| { - let create_time = { - let rfc3339 = patch_rfc3339(&news.create_time); - let dt = DateTime::parse_from_rfc3339(&rfc3339) - .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) - .with_timezone(&Utc); - fmt_time_to_string_friendly(dt.into(), Local::now()) - }; - + let create_time = display_cnb_time(&news.create_time, time_style); let url = format!("https://news.cnblogs.com/n/{}", news.id); println!("{} {}", create_time, url); println!(" {}", news.title); diff --git a/src/infra/time.rs b/src/infra/time.rs index 8331612..5ccc42c 100644 --- a/src/infra/time.rs +++ b/src/infra/time.rs @@ -1,10 +1,23 @@ -use chrono::{DateTime, Datelike, TimeZone, Timelike}; +use crate::args::TimeStyle; +use chrono::{DateTime, Datelike, Local, TimeZone, Timelike, Utc}; use std::fmt::Display; +pub fn display_cnb_time(time_str: &str, time_style: &TimeStyle) -> String { + let rfc3339 = patch_rfc3339(time_str); + let dt = DateTime::parse_from_rfc3339(&rfc3339) + .unwrap_or_else(|_| panic!("Invalid RFC3339: {}", rfc3339)) + .with_timezone(&Utc); + + match time_style { + TimeStyle::Friendly => fmt_time_to_string_friendly(dt.into(), Local::now()), + TimeStyle::Normal => dt.format("%y-%-m-%-d %-H:%M").to_string(), + } +} + // HACK: // Sometimes cnblogs' web API returns time string like: "2023-09-12T14:07:00" or "2019-02-06T08:45:53.94" // This will patch it to standard RFC3339 format -pub fn patch_rfc3339(time_str: &str) -> String { +fn patch_rfc3339(time_str: &str) -> String { if time_str.len() != 25 { let u8vec: Vec<_> = time_str.bytes().take(19).collect(); format!( @@ -17,7 +30,7 @@ pub fn patch_rfc3339(time_str: &str) -> String { } } -pub fn fmt_time_to_string_friendly(time_to_fmt: DateTime, current_time: DateTime) -> String +fn fmt_time_to_string_friendly(time_to_fmt: DateTime, current_time: DateTime) -> String where T: TimeZone, ::Offset: Display, diff --git a/src/main.rs b/src/main.rs index 1eb405d..d28b7b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,7 @@ async fn main() -> Result<()> { let pat = args.with_pat.clone().or_eval_result(session::get_pat); let style = &args.style; + let time_style = &args.time_style; let rev = args.rev; let foe = args.fail_on_error; let quiet = args.quiet; @@ -82,7 +83,7 @@ async fn main() -> Result<()> { .collect::>>()? }; foe.then(|| panic_if_err(&ing_with_comment_list)); - quiet.not().then(|| display::list_ing(style, &ing_with_comment_list, rev, align)); + quiet.not().then(|| display::list_ing(style, time_style, &ing_with_comment_list, rev, align)); } _ if let Some(content) = parser::publish_ing(&args) => { let content = try { @@ -108,15 +109,15 @@ async fn main() -> Result<()> { _ if let Some(id) = parser::show_post_meta(&args) => { let entry = Post::new(pat?).get_one(id).await; foe.then(|| panic_if_err(&entry)); - quiet.not().then(|| display::show_post_meta(style, &entry)); + quiet.not().then(|| display::show_post_meta(style, time_style, &entry)); } _ if let Some(id) = parser::show_post_comment(&args) => { let comment_vec = Post::new(pat?).get_comment_list(id).await; foe.then(|| panic_if_err(&comment_vec)); - quiet.not().then(|| display::show_post_comment(style, &comment_vec, rev)); + quiet.not().then(|| display::show_post_comment(style, time_style, &comment_vec, rev)); } _ if let Some((skip, take)) = parser::list_post(&args) => { - let meta_vec = Post::new(pat?).get_meta_list(skip,take).await; + let meta_vec = Post::new(pat?).get_meta_list(skip, take).await; foe.then(|| panic_if_err(&meta_vec)); quiet.not().then(|| display::list_post(style, &meta_vec, rev)); } @@ -146,7 +147,7 @@ async fn main() -> Result<()> { _ if let Some((skip, take)) = parser::list_news(&args) => { let news_vec = News::new(pat?).get_list(skip, take).await; foe.then(|| panic_if_err(&news_vec)); - quiet.not().then(|| display::list_news(style, &news_vec, rev)); + quiet.not().then(|| display::list_news(style, time_style, &news_vec, rev)); } _ if no_operation(&args) => {