diff --git a/src/args/mod.rs b/src/args/mod.rs index d74b534..2a69d14 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -80,7 +80,6 @@ pub struct GlobalOpt { pub quiet: bool, } -// TODO: flatten options in struct? #[derive(Debug, Parser)] #[command(author, about, long_about = None, version)] pub struct Args { diff --git a/src/display/colorful.rs b/src/display/colorful.rs deleted file mode 100644 index 4d9c62e..0000000 --- a/src/display/colorful.rs +++ /dev/null @@ -1,379 +0,0 @@ -use crate::api::fav::get_list::FavEntry; -use crate::api::ing::get_comment_list::IngCommentEntry; -use crate::api::ing::get_list::IngEntry; -use crate::api::ing::{ - fmt_content, get_ing_at_user_tag_text, ing_star_tag_to_text, rm_ing_at_user_tag, IngSendFrom, -}; -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::result::IntoResult; -use crate::infra::str::StrExt; -use crate::infra::terminal::get_term_width; -use crate::infra::time::display_cnb_time; -use anyhow::Result; -use colored::Colorize; -use std::fmt::Display; -use std::fmt::Write; -use std::ops::Not; -use std::path::PathBuf; -use unicode_width::UnicodeWidthStr; - -#[inline] -pub fn fmt_err(e: &anyhow::Error) -> String { - format!("{}: {}", "Err".red(), e) -} - -#[inline] -pub fn fmt_result(result: &Result) -> String { - match result { - Ok(t) => format!("{}: {}", "Ok".green(), t), - Err(e) => fmt_err(e), - } -} - -pub fn login(cfg_path: &Result) -> String { - match cfg_path { - Ok(pb) => format!("PAT was saved in {:?}", pb), - Err(e) => fmt_err(e), - } -} - -pub fn logout(cfg_path: &Result) -> String { - match cfg_path { - Ok(pb) => format!("{:?} was successfully removed", pb), - Err(e) => fmt_err(e), - } -} - -pub fn user_info(info: &Result) -> Result { - let info = match info { - Ok(info) => info, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - write!(buf, "{}", info.display_name.cyan())?; - if info.is_vip { - write!(buf, " {}", " VIP ".on_blue())?; - } - writeln!(buf)?; - writeln!( - buf, - "{} Following {} Followers", - info.following_count, info.followers_count - )?; - writeln!(buf, "ID {}", info.blog_id)?; - writeln!(buf, "Joined {}", info.joined)?; - writeln!(buf, "Blog https://www.cnblogs.com/{}", info.blog_app)?; - } - buf.into_ok() -} - -// TODO: rm unnecessary line divider -pub fn list_ing( - time_style: &TimeStyle, - ing_with_comment_iter: Result)>>, - align: bool, -) -> Result { - let mut ing_with_comment_iter = match ing_with_comment_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - ing_with_comment_iter.try_fold(String::new(), |mut buf, (ing, comment_list)| try { - { - let buf = &mut buf; - let create_time = display_cnb_time(&ing.create_time, time_style); - write!(buf, "{}", create_time.dimmed())?; - - let send_from_mark = match ing.send_from { - IngSendFrom::Cli => Some("CLI"), - IngSendFrom::CellPhone => Some("Mobile"), - IngSendFrom::VsCode => Some("VSCode"), - IngSendFrom::Web => Some("Web"), - _ => None, - }; - if let Some(mark) = send_from_mark { - write!(buf, " {}", mark.dimmed())?; - } - if ing.is_lucky { - let star_text = ing_star_tag_to_text(&ing.icons); - write!(buf, " {}⭐", star_text.yellow())?; - } - writeln!(buf, " {} {}", "#".dimmed(), ing.id.to_string().dimmed())?; - let content = if align { - let user_name_width = ing.user_name.width_cjk(); - let left_width = get_term_width().saturating_sub(user_name_width + 3); - fmt_content(&ing.content) - .width_split(left_width) - .map_or_else( - || ing.content.clone(), - |lines| { - if comment_list.is_empty().not() { - lines.join("\n").replace( - '\n', - &format!("\n │{}", " ".repeat(user_name_width - 2)), - ) - } else { - lines.join("\n").replace( - '\n', - &format!("\n{}", " ".repeat(user_name_width + 3)), - ) - } - }, - ) - } else { - fmt_content(&ing.content) - }; - writeln!(buf, " {} {}", ing.user_name.cyan(), content)?; - - let len = comment_list.len(); - if len != 0 { - let max_i = len - 1; - let comment_list_buf: Result = comment_list.iter().enumerate().try_fold( - String::new(), - |mut buf, (i, entry)| try { - { - let buf = &mut buf; - if i != max_i { - write!(buf, " │ {}", entry.user_name.blue())?; - } else { - write!(buf, " └ {}", entry.user_name.blue())?; - } - let at_user = get_ing_at_user_tag_text(&entry.content); - if at_user.is_empty().not() { - write!(buf, " {}{}", "@".bright_black(), at_user.bright_black())?; - } - let content = { - let content = rm_ing_at_user_tag(&entry.content); - fmt_content(&content) - }; - writeln!(buf, " {}", content.dimmed())?; - } - buf - }, - ); - write!(buf, "{}", comment_list_buf?)?; - } - - writeln!(buf)?; - }; - buf - }) -} - -pub fn show_post(entry: &Result) -> Result { - let entry = match entry { - Ok(entry) => entry, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - writeln!(buf, "{}\n", entry.title.cyan().bold())?; - if let Some(body) = &entry.body { - writeln!(buf, "{}", body)?; - } - } - buf.into_ok() -} - -pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) -> Result { - let entry = match entry { - Ok(entry) => entry, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - writeln!(buf, "Title {}", entry.title.cyan().bold())?; - { - write!(buf, "Status")?; - if entry.is_published { - write!(buf, " {}", "Published".green())?; - } else { - write!(buf, " {}", "Draft".yellow())?; - } - if entry.is_pinned { - write!(buf, " {}", "Pinned".magenta())?; - } - writeln!(buf)?; - }; - if let Some(body) = &entry.body { - let words_count = words_count::count(body).words; - writeln!(buf, "Words {}", words_count)?; - } - if let Some(tags) = &entry.tags { - if let Some(tags_text) = tags - .clone() - .into_iter() - .reduce(|acc, tag| format!("{}, {}", acc, tag)) - { - writeln!(buf, "Tags {}", tags_text)?; - } - } - let create_time = display_cnb_time(&entry.create_time, time_style); - writeln!(buf, "Create {}", create_time)?; - let modify_time = display_cnb_time(&entry.create_time, time_style); - writeln!(buf, "Modify {}", modify_time)?; - writeln!(buf, "Link https:{}", entry.url)?; - } - buf.into_ok() -} - -pub fn show_post_comment( - time_style: &TimeStyle, - comment_iter: Result>, -) -> Result { - let mut comment_iter = match comment_iter { - Ok(entry) => entry, - Err(e) => return fmt_err(&e).into_ok(), - }; - - comment_iter.try_fold(String::new(), |mut buf, comment| try { - { - let buf = &mut buf; - let create_time = display_cnb_time(&comment.create_time, time_style); - let floor_text = format!("{}F", comment.floor); - writeln!(buf, "{} {}", create_time.dimmed(), floor_text.dimmed())?; - writeln!(buf, " {} {}", comment.user_name.cyan(), comment.content)?; - } - buf - }) -} - -pub fn list_post( - result: Result<(impl ExactSizeIterator, usize)>, -) -> Result { - let (mut entry_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - entry_iter.try_fold( - format!("{}/{}\n", entry_iter.len(), total_count), - |mut buf, entry| try { - { - let buf = &mut buf; - write!(buf, "{} {}", "#".dimmed(), entry.id.to_string().dimmed())?; - write!(buf, " {}", entry.title.cyan().bold())?; - if entry.is_published { - write!(buf, " {}", "Pub".green())?; - } else { - write!(buf, " {}", "Dft".yellow())?; - } - if entry.is_pinned { - write!(buf, " {}", "Pin".magenta())?; - } - writeln!(buf)?; - } - buf - }, - ) -} - -pub fn search_post( - result: Result<(impl ExactSizeIterator, usize)>, -) -> Result { - let (mut id_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - id_iter.try_fold( - format!("{}/{}\n", id_iter.len(), total_count), - |mut buf, id| try { - writeln!(&mut buf, "# {}", id)?; - buf - }, - ) -} - -pub fn list_news( - time_style: &TimeStyle, - news_iter: Result>, -) -> Result { - let news_iter = match news_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - news_iter - .map(|news| try { - let mut buf = String::new(); - { - let buf = &mut buf; - let create_time = display_cnb_time(&news.create_time, time_style); - let url = format!("https://news.cnblogs.com/n/{}", news.id); - writeln!(buf, "{} {}", create_time.dimmed(), url.dimmed())?; - writeln!(buf, " {}", news.title)?; - - let summary = { - let summary = format!("{}...", news.summary); - summary.width_split(get_term_width() - 4).map_or_else( - || summary.clone(), - |vec| { - vec.into_iter() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - }, - ) - }; - writeln!(buf, "{}", summary.dimmed())?; - } - buf - }) - .try_fold(String::new(), |mut acc, buf: Result| try { - write!(&mut acc, "\n{}", buf?)?; - acc - }) -} - -pub fn list_fav( - time_style: &TimeStyle, - fav_iter: Result>, -) -> Result { - let fav_iter = match fav_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - fav_iter - .map(|fav| try { - let mut buf = String::new(); - { - let buf = &mut buf; - let create_time = display_cnb_time(&fav.create_time, time_style); - writeln!(buf, "{} {}", create_time.dimmed(), fav.url.dimmed())?; - writeln!(buf, " {}", fav.title)?; - - let summary = { - fav.summary.width_split(get_term_width() - 4).map_or_else( - || fav.summary.clone(), - |vec| { - vec.into_iter() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - }, - ) - }; - if summary.is_empty().not() { - writeln!(buf, "{}", summary.dimmed())?; - } - } - buf - }) - .try_fold(String::new(), |mut acc, buf: Result| try { - write!(&mut acc, "\n{}", buf?)?; - acc - }) -} diff --git a/src/display/colorful/fav.rs b/src/display/colorful/fav.rs new file mode 100644 index 0000000..39a5389 --- /dev/null +++ b/src/display/colorful/fav.rs @@ -0,0 +1,52 @@ +use crate::api::fav::get_list::FavEntry; +use crate::args::TimeStyle; +use crate::display::colorful::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use colored::Colorize; +use std::fmt::Write; +use std::ops::Not; + +pub fn list_fav( + time_style: &TimeStyle, + fav_iter: Result>, +) -> Result { + let fav_iter = match fav_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + fav_iter + .map(|fav| try { + let mut buf = String::new(); + { + let buf = &mut buf; + let create_time = display_cnb_time(&fav.create_time, time_style); + writeln!(buf, "{} {}", create_time.dimmed(), fav.url.dimmed())?; + writeln!(buf, " {}", fav.title)?; + + let summary = { + fav.summary.width_split(get_term_width() - 4).map_or_else( + || fav.summary.clone(), + |vec| { + vec.into_iter() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + }, + ) + }; + if summary.is_empty().not() { + writeln!(buf, "{}", summary.dimmed())?; + } + } + buf + }) + .try_fold(String::new(), |mut acc, buf: Result| try { + write!(&mut acc, "\n{}", buf?)?; + acc + }) +} diff --git a/src/display/colorful/ing.rs b/src/display/colorful/ing.rs new file mode 100644 index 0000000..71d9461 --- /dev/null +++ b/src/display/colorful/ing.rs @@ -0,0 +1,109 @@ +use crate::api::ing::get_comment_list::IngCommentEntry; +use crate::api::ing::get_list::IngEntry; +use crate::api::ing::{ + fmt_content, get_ing_at_user_tag_text, ing_star_tag_to_text, rm_ing_at_user_tag, IngSendFrom, +}; +use crate::args::TimeStyle; +use crate::display::colorful::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use colored::Colorize; +use std::fmt::Write; +use std::ops::Not; +use unicode_width::UnicodeWidthStr; + +// TODO: rm unnecessary line divider +pub fn list_ing( + time_style: &TimeStyle, + ing_with_comment_iter: Result)>>, + align: bool, +) -> Result { + let mut ing_with_comment_iter = match ing_with_comment_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + ing_with_comment_iter.try_fold(String::new(), |mut buf, (ing, comment_list)| try { + { + let buf = &mut buf; + let create_time = display_cnb_time(&ing.create_time, time_style); + write!(buf, "{}", create_time.dimmed())?; + + let send_from_mark = match ing.send_from { + IngSendFrom::Cli => Some("CLI"), + IngSendFrom::CellPhone => Some("Mobile"), + IngSendFrom::VsCode => Some("VSCode"), + IngSendFrom::Web => Some("Web"), + _ => None, + }; + if let Some(mark) = send_from_mark { + write!(buf, " {}", mark.dimmed())?; + } + if ing.is_lucky { + let star_text = ing_star_tag_to_text(&ing.icons); + write!(buf, " {}⭐", star_text.yellow())?; + } + writeln!(buf, " {} {}", "#".dimmed(), ing.id.to_string().dimmed())?; + let content = if align { + let user_name_width = ing.user_name.width_cjk(); + let left_width = get_term_width().saturating_sub(user_name_width + 3); + fmt_content(&ing.content) + .width_split(left_width) + .map_or_else( + || ing.content.clone(), + |lines| { + if comment_list.is_empty().not() { + lines.join("\n").replace( + '\n', + &format!("\n │{}", " ".repeat(user_name_width - 2)), + ) + } else { + lines.join("\n").replace( + '\n', + &format!("\n{}", " ".repeat(user_name_width + 3)), + ) + } + }, + ) + } else { + fmt_content(&ing.content) + }; + writeln!(buf, " {} {}", ing.user_name.cyan(), content)?; + + let len = comment_list.len(); + if len != 0 { + let max_i = len - 1; + let comment_list_buf: Result = comment_list.iter().enumerate().try_fold( + String::new(), + |mut buf, (i, entry)| try { + { + let buf = &mut buf; + if i != max_i { + write!(buf, " │ {}", entry.user_name.blue())?; + } else { + write!(buf, " └ {}", entry.user_name.blue())?; + } + let at_user = get_ing_at_user_tag_text(&entry.content); + if at_user.is_empty().not() { + write!(buf, " {}{}", "@".bright_black(), at_user.bright_black())?; + } + let content = { + let content = rm_ing_at_user_tag(&entry.content); + fmt_content(&content) + }; + writeln!(buf, " {}", content.dimmed())?; + } + buf + }, + ); + write!(buf, "{}", comment_list_buf?)?; + } + + writeln!(buf)?; + }; + buf + }) +} diff --git a/src/display/colorful/mod.rs b/src/display/colorful/mod.rs new file mode 100644 index 0000000..ca1e682 --- /dev/null +++ b/src/display/colorful/mod.rs @@ -0,0 +1,22 @@ +pub mod fav; +pub mod ing; +pub mod news; +pub mod post; +pub mod user; + +use anyhow::Result; +use colored::Colorize; +use std::fmt::Display; + +#[inline] +pub fn fmt_err(e: &anyhow::Error) -> String { + format!("{}: {}", "Err".red(), e) +} + +#[inline] +pub fn fmt_result(result: &Result) -> String { + match result { + Ok(t) => format!("{}: {}", "Ok".green(), t), + Err(e) => fmt_err(e), + } +} diff --git a/src/display/colorful/news.rs b/src/display/colorful/news.rs new file mode 100644 index 0000000..7208ae6 --- /dev/null +++ b/src/display/colorful/news.rs @@ -0,0 +1,51 @@ +use crate::api::news::get_list::NewsEntry; +use crate::args::TimeStyle; +use crate::display::colorful::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use colored::Colorize; +use std::fmt::Write; + +pub fn list_news( + time_style: &TimeStyle, + news_iter: Result>, +) -> Result { + let news_iter = match news_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + news_iter + .map(|news| try { + let mut buf = String::new(); + { + let buf = &mut buf; + let create_time = display_cnb_time(&news.create_time, time_style); + let url = format!("https://news.cnblogs.com/n/{}", news.id); + writeln!(buf, "{} {}", create_time.dimmed(), url.dimmed())?; + writeln!(buf, " {}", news.title)?; + + let summary = { + let summary = format!("{}...", news.summary); + summary.width_split(get_term_width() - 4).map_or_else( + || summary.clone(), + |vec| { + vec.into_iter() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + }, + ) + }; + writeln!(buf, "{}", summary.dimmed())?; + } + buf + }) + .try_fold(String::new(), |mut acc, buf: Result| try { + write!(&mut acc, "\n{}", buf?)?; + acc + }) +} diff --git a/src/display/colorful/post.rs b/src/display/colorful/post.rs new file mode 100644 index 0000000..60bda5e --- /dev/null +++ b/src/display/colorful/post.rs @@ -0,0 +1,138 @@ +use crate::api::post::get_comment_list::PostCommentEntry; +use crate::api::post::get_one::PostEntry; +use crate::args::TimeStyle; +use crate::display::colorful::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use colored::Colorize; +use std::fmt::Write; + +pub fn list_post( + result: Result<(impl ExactSizeIterator, usize)>, +) -> Result { + let (mut entry_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + entry_iter.try_fold( + format!("{}/{}\n", entry_iter.len(), total_count), + |mut buf, entry| try { + { + let buf = &mut buf; + write!(buf, "{} {}", "#".dimmed(), entry.id.to_string().dimmed())?; + write!(buf, " {}", entry.title.cyan().bold())?; + if entry.is_published { + write!(buf, " {}", "Pub".green())?; + } else { + write!(buf, " {}", "Dft".yellow())?; + } + if entry.is_pinned { + write!(buf, " {}", "Pin".magenta())?; + } + writeln!(buf)?; + } + buf + }, + ) +} + +pub fn show_post(entry: &Result) -> Result { + let entry = match entry { + Ok(entry) => entry, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + writeln!(buf, "{}\n", entry.title.cyan().bold())?; + if let Some(body) = &entry.body { + writeln!(buf, "{}", body)?; + } + } + buf.into_ok() +} + +pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) -> Result { + let entry = match entry { + Ok(entry) => entry, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + writeln!(buf, "Title {}", entry.title.cyan().bold())?; + { + write!(buf, "Status")?; + if entry.is_published { + write!(buf, " {}", "Published".green())?; + } else { + write!(buf, " {}", "Draft".yellow())?; + } + if entry.is_pinned { + write!(buf, " {}", "Pinned".magenta())?; + } + writeln!(buf)?; + }; + if let Some(body) = &entry.body { + let words_count = words_count::count(body).words; + writeln!(buf, "Words {}", words_count)?; + } + if let Some(tags) = &entry.tags { + if let Some(tags_text) = tags + .clone() + .into_iter() + .reduce(|acc, tag| format!("{}, {}", acc, tag)) + { + writeln!(buf, "Tags {}", tags_text)?; + } + } + let create_time = display_cnb_time(&entry.create_time, time_style); + writeln!(buf, "Create {}", create_time)?; + let modify_time = display_cnb_time(&entry.create_time, time_style); + writeln!(buf, "Modify {}", modify_time)?; + writeln!(buf, "Link https:{}", entry.url)?; + } + buf.into_ok() +} + +pub fn show_post_comment( + time_style: &TimeStyle, + comment_iter: Result>, +) -> Result { + let mut comment_iter = match comment_iter { + Ok(entry) => entry, + Err(e) => return fmt_err(&e).into_ok(), + }; + + comment_iter.try_fold(String::new(), |mut buf, comment| try { + { + let buf = &mut buf; + let create_time = display_cnb_time(&comment.create_time, time_style); + let floor_text = format!("{}F", comment.floor); + writeln!(buf, "{} {}", create_time.dimmed(), floor_text.dimmed())?; + writeln!(buf, " {} {}", comment.user_name.cyan(), comment.content)?; + } + buf + }) +} + +pub fn search_post( + result: Result<(impl ExactSizeIterator, usize)>, +) -> Result { + let (mut id_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + id_iter.try_fold( + format!("{}/{}\n", id_iter.len(), total_count), + |mut buf, id| try { + writeln!(&mut buf, "# {}", id)?; + buf + }, + ) +} diff --git a/src/display/colorful/user.rs b/src/display/colorful/user.rs new file mode 100644 index 0000000..0e61ad4 --- /dev/null +++ b/src/display/colorful/user.rs @@ -0,0 +1,47 @@ +use crate::api::user::info::UserInfo; +use crate::display::colorful::fmt_err; +use crate::infra::result::IntoResult; +use anyhow::Result; +use colored::Colorize; +use std::fmt::Write; +use std::path::PathBuf; + +pub fn login(cfg_path: &Result) -> String { + match cfg_path { + Ok(pb) => format!("PAT was saved in {:?}", pb), + Err(e) => fmt_err(e), + } +} + +pub fn logout(cfg_path: &Result) -> String { + match cfg_path { + Ok(pb) => format!("{:?} was successfully removed", pb), + Err(e) => fmt_err(e), + } +} + +pub fn user_info(info: &Result) -> Result { + let info = match info { + Ok(info) => info, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + write!(buf, "{}", info.display_name.cyan())?; + if info.is_vip { + write!(buf, " {}", " VIP ".on_blue())?; + } + writeln!(buf)?; + writeln!( + buf, + "{} Following {} Followers", + info.following_count, info.followers_count + )?; + writeln!(buf, "ID {}", info.blog_id)?; + writeln!(buf, "Joined {}", info.joined)?; + writeln!(buf, "Blog https://www.cnblogs.com/{}", info.blog_app)?; + } + buf.into_ok() +} diff --git a/src/display/json.rs b/src/display/json.rs deleted file mode 100644 index e7630a2..0000000 --- a/src/display/json.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::api::fav::get_list::FavEntry; -use crate::api::ing::get_comment_list::IngCommentEntry; -use crate::api::ing::get_list::IngEntry; -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::infra::json; -use crate::infra::result::IntoResult; -use anyhow::Result; -use serde::Serialize; -use serde_json::json; -use std::path::PathBuf; - -#[inline] -pub fn fmt_err(e: &anyhow::Error) -> String { - let json = json!({ - "is_ok": false, - "msg": e.to_string() - }); - json.to_string() -} - -pub fn login(cfg_path: &Result) -> String { - let json = cfg_path.as_ref().map(|pb| json!({"cfg_path":pb})); - fmt_result(&json) -} - -pub fn logout(cfg_path: &Result) -> String { - let json = cfg_path.as_ref().map(|pb| json!({"cfg_path":pb})); - fmt_result(&json) -} - -pub fn user_info(info: &Result) -> String { - fmt_result(info) -} - -pub fn list_ing( - ing_with_comment_list: Result)>>, -) -> Result { - let ing_with_comment_list = match ing_with_comment_list { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - let json_vec = ing_with_comment_list - .map(|(entry, comment_list)| { - json!({ - "entry": entry, - "comment_list": comment_list - }) - }) - .collect::>(); - - json::serialize(json_vec) -} - -pub fn show_post(entry: &Result) -> String { - let json = entry.as_ref().map(|entry| { - json!({ - "title": entry.title, - "body": entry.body - }) - }); - fmt_result(&json) -} - -pub fn show_post_meta(entry: &Result) -> String { - fmt_result(entry) -} - -pub fn show_post_comment( - comment_iter: Result>, -) -> Result { - let comment_iter = match comment_iter { - Ok(entry) => entry, - Err(e) => return fmt_err(&e).into_ok(), - }; - - let comment_vec = comment_iter.collect::>(); - json::serialize(comment_vec) -} - -pub fn list_post(result: Result<(impl ExactSizeIterator, usize)>) -> String { - let (entry_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e), - }; - - let vec = entry_iter.collect::>(); - let json = json!({ - "listed_count": vec.len(), - "total_count": total_count, - "entry_list": vec, - }); - json.to_string() -} - -pub fn search_post(result: Result<(impl ExactSizeIterator, usize)>) -> String { - let (id_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e), - }; - - let id_list = id_iter.collect::>(); - let json = json!({ - "listed_count": id_list.len(), - "total_count": total_count, - "id_list": id_list, - }); - json.to_string() -} - -pub fn fmt_result(result: &Result) -> String { - let json = match result { - Ok(t) => json!({ - "is_ok": true, - "msg": t - }), - Err(e) => json!({ - "is_ok": false, - "msg": e.to_string() - }), - }; - json.to_string() -} - -pub fn list_news(news_iter: Result>) -> Result { - let news_iter = match news_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - let vec = news_iter.collect::>(); - - json::serialize(vec) -} - -pub fn list_fav(fav_iter: Result>) -> Result { - let fav_iter = match fav_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - let vec = fav_iter.collect::>(); - - json::serialize(vec) -} diff --git a/src/display/json/fav.rs b/src/display/json/fav.rs new file mode 100644 index 0000000..816d5f0 --- /dev/null +++ b/src/display/json/fav.rs @@ -0,0 +1,16 @@ +use crate::api::fav::get_list::FavEntry; +use crate::display::json::fmt_err; +use crate::infra::json; +use crate::infra::result::IntoResult; +use anyhow::Result; + +pub fn list_fav(fav_iter: Result>) -> Result { + let fav_iter = match fav_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + let vec = fav_iter.collect::>(); + + json::serialize(vec) +} diff --git a/src/display/json/ing.rs b/src/display/json/ing.rs new file mode 100644 index 0000000..a43546d --- /dev/null +++ b/src/display/json/ing.rs @@ -0,0 +1,27 @@ +use crate::api::ing::get_comment_list::IngCommentEntry; +use crate::api::ing::get_list::IngEntry; +use crate::display::json::fmt_err; +use crate::infra::json; +use crate::infra::result::IntoResult; +use anyhow::Result; +use serde_json::json; + +pub fn list_ing( + ing_with_comment_list: Result)>>, +) -> Result { + let ing_with_comment_list = match ing_with_comment_list { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + let json_vec = ing_with_comment_list + .map(|(entry, comment_list)| { + json!({ + "entry": entry, + "comment_list": comment_list + }) + }) + .collect::>(); + + json::serialize(json_vec) +} diff --git a/src/display/json/mod.rs b/src/display/json/mod.rs new file mode 100644 index 0000000..3f3d49d --- /dev/null +++ b/src/display/json/mod.rs @@ -0,0 +1,32 @@ +pub mod fav; +pub mod ing; +pub mod news; +pub mod post; +pub mod user; + +use anyhow::Result; +use serde::Serialize; +use serde_json::json; + +#[inline] +pub fn fmt_err(e: &anyhow::Error) -> String { + let json = json!({ + "is_ok": false, + "msg": e.to_string() + }); + json.to_string() +} + +pub fn fmt_result(result: &Result) -> String { + let json = match result { + Ok(t) => json!({ + "is_ok": true, + "msg": t + }), + Err(e) => json!({ + "is_ok": false, + "msg": e.to_string() + }), + }; + json.to_string() +} diff --git a/src/display/json/news.rs b/src/display/json/news.rs new file mode 100644 index 0000000..3488c83 --- /dev/null +++ b/src/display/json/news.rs @@ -0,0 +1,16 @@ +use crate::api::news::get_list::NewsEntry; +use crate::display::json::fmt_err; +use crate::infra::json; +use crate::infra::result::IntoResult; +use anyhow::Result; + +pub fn list_news(news_iter: Result>) -> Result { + let news_iter = match news_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + let vec = news_iter.collect::>(); + + json::serialize(vec) +} diff --git a/src/display/json/post.rs b/src/display/json/post.rs new file mode 100644 index 0000000..8f804d4 --- /dev/null +++ b/src/display/json/post.rs @@ -0,0 +1,63 @@ +use crate::api::post::get_comment_list::PostCommentEntry; +use crate::api::post::get_one::PostEntry; +use crate::display::json::{fmt_err, fmt_result}; +use crate::infra::json; +use crate::infra::result::IntoResult; +use anyhow::Result; +use serde_json::json; + +pub fn list_post(result: Result<(impl ExactSizeIterator, usize)>) -> String { + let (entry_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e), + }; + + let vec = entry_iter.collect::>(); + let json = json!({ + "listed_count": vec.len(), + "total_count": total_count, + "entry_list": vec, + }); + json.to_string() +} + +pub fn show_post(entry: &Result) -> String { + let json = entry.as_ref().map(|entry| { + json!({ + "title": entry.title, + "body": entry.body + }) + }); + fmt_result(&json) +} + +pub fn show_post_meta(entry: &Result) -> String { + fmt_result(entry) +} + +pub fn show_post_comment( + comment_iter: Result>, +) -> Result { + let comment_iter = match comment_iter { + Ok(entry) => entry, + Err(e) => return fmt_err(&e).into_ok(), + }; + + let comment_vec = comment_iter.collect::>(); + json::serialize(comment_vec) +} + +pub fn search_post(result: Result<(impl ExactSizeIterator, usize)>) -> String { + let (id_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e), + }; + + let id_list = id_iter.collect::>(); + let json = json!({ + "listed_count": id_list.len(), + "total_count": total_count, + "id_list": id_list, + }); + json.to_string() +} diff --git a/src/display/json/user.rs b/src/display/json/user.rs new file mode 100644 index 0000000..9552003 --- /dev/null +++ b/src/display/json/user.rs @@ -0,0 +1,19 @@ +use crate::api::user::info::UserInfo; +use crate::display::json::fmt_result; +use anyhow::Result; +use serde_json::json; +use std::path::PathBuf; + +pub fn login(cfg_path: &Result) -> String { + let json = cfg_path.as_ref().map(|pb| json!({"cfg_path":pb})); + fmt_result(&json) +} + +pub fn logout(cfg_path: &Result) -> String { + let json = cfg_path.as_ref().map(|pb| json!({"cfg_path":pb})); + fmt_result(&json) +} + +pub fn user_info(info: &Result) -> String { + fmt_result(info) +} diff --git a/src/display/mod.rs b/src/display/mod.rs index 7cfd0c6..b7e1090 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -16,25 +16,25 @@ mod normal; pub fn login(style: &Style, cfg_path: &Result) -> String { match style { - Style::Colorful => colorful::login(cfg_path), - Style::Normal => normal::login(cfg_path), - Style::Json => json::login(cfg_path), + Style::Colorful => colorful::user::login(cfg_path), + Style::Normal => normal::user::login(cfg_path), + Style::Json => json::user::login(cfg_path), } } pub fn logout(style: &Style, cfg_path: &Result) -> String { match style { - Style::Colorful => colorful::logout(cfg_path), - Style::Normal => normal::logout(cfg_path), - Style::Json => json::logout(cfg_path), + Style::Colorful => colorful::user::logout(cfg_path), + Style::Normal => normal::user::logout(cfg_path), + Style::Json => json::user::logout(cfg_path), } } pub fn user_info(style: &Style, user_info: &Result) -> Result { match style { - Style::Colorful => colorful::user_info(user_info), - Style::Normal => normal::user_info(user_info), - Style::Json => json::user_info(user_info).into_ok(), + Style::Colorful => colorful::user::user_info(user_info), + Style::Normal => normal::user::user_info(user_info), + Style::Json => json::user::user_info(user_info).into_ok(), } } @@ -45,9 +45,9 @@ pub fn list_ing( align: bool, ) -> Result { match style { - Style::Colorful => colorful::list_ing(time_style, ing_with_comment_iter, align), - Style::Normal => normal::list_ing(time_style, ing_with_comment_iter, align), - Style::Json => json::list_ing(ing_with_comment_iter), + Style::Colorful => colorful::ing::list_ing(time_style, ing_with_comment_iter, align), + Style::Normal => normal::ing::list_ing(time_style, ing_with_comment_iter, align), + Style::Json => json::ing::list_ing(ing_with_comment_iter), } } @@ -69,44 +69,44 @@ pub fn comment_ing(style: &Style, result: &Result<&String>) -> String { pub fn show_post(style: &Style, entry: &Result) -> Result { match style { - Style::Colorful => colorful::show_post(entry), - Style::Normal => normal::show_post(entry), - Style::Json => json::show_post(entry).into_ok(), + Style::Colorful => colorful::post::show_post(entry), + Style::Normal => normal::post::show_post(entry), + Style::Json => json::post::show_post(entry).into_ok(), } } -pub fn show_post_meta( +pub fn list_post( style: &Style, - time_style: &TimeStyle, - entry: &Result, + result: Result<(impl ExactSizeIterator, usize)>, ) -> Result { match style { - 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).into_ok(), + Style::Colorful => colorful::post::list_post(result), + Style::Normal => normal::post::list_post(result), + Style::Json => json::post::list_post(result).into_ok(), } } -pub fn show_post_comment( +pub fn show_post_meta( style: &Style, time_style: &TimeStyle, - comment_iter: Result>, + entry: &Result, ) -> Result { match style { - Style::Colorful => colorful::show_post_comment(time_style, comment_iter), - Style::Normal => normal::show_post_comment(time_style, comment_iter), - Style::Json => json::show_post_comment(comment_iter), + Style::Colorful => colorful::post::show_post_meta(time_style, entry), + Style::Normal => normal::post::show_post_meta(time_style, entry), + Style::Json => json::post::show_post_meta(entry).into_ok(), } } -pub fn list_post( +pub fn show_post_comment( style: &Style, - result: Result<(impl ExactSizeIterator, usize)>, + time_style: &TimeStyle, + comment_iter: Result>, ) -> Result { match style { - Style::Colorful => colorful::list_post(result), - Style::Normal => normal::list_post(result), - Style::Json => json::list_post(result).into_ok(), + Style::Colorful => colorful::post::show_post_comment(time_style, comment_iter), + Style::Normal => normal::post::show_post_comment(time_style, comment_iter), + Style::Json => json::post::show_post_comment(comment_iter), } } @@ -123,9 +123,9 @@ pub fn search_post( result: Result<(impl ExactSizeIterator, usize)>, ) -> Result { match style { - Style::Colorful => colorful::search_post(result), - Style::Normal => normal::search_post(result), - Style::Json => json::search_post(result).into_ok(), + Style::Colorful => colorful::post::search_post(result), + Style::Normal => normal::post::search_post(result), + Style::Json => json::post::search_post(result).into_ok(), } } @@ -151,9 +151,9 @@ pub fn list_news( news_iter: Result>, ) -> Result { match style { - Style::Colorful => colorful::list_news(time_style, news_iter), - Style::Normal => normal::list_news(time_style, news_iter), - Style::Json => json::list_news(news_iter), + Style::Colorful => colorful::news::list_news(time_style, news_iter), + Style::Normal => normal::news::list_news(time_style, news_iter), + Style::Json => json::news::list_news(news_iter), } } @@ -163,8 +163,8 @@ pub fn list_fav( fav_iter: Result>, ) -> Result { match style { - Style::Colorful => colorful::list_fav(time_style, fav_iter), - Style::Normal => normal::list_fav(time_style, fav_iter), - Style::Json => json::list_fav(fav_iter), + Style::Colorful => colorful::fav::list_fav(time_style, fav_iter), + Style::Normal => normal::fav::list_fav(time_style, fav_iter), + Style::Json => json::fav::list_fav(fav_iter), } } diff --git a/src/display/normal.rs b/src/display/normal.rs deleted file mode 100644 index f9dbfb8..0000000 --- a/src/display/normal.rs +++ /dev/null @@ -1,376 +0,0 @@ -use crate::api::fav::get_list::FavEntry; -use crate::api::ing::get_comment_list::IngCommentEntry; -use crate::api::ing::get_list::IngEntry; -use crate::api::ing::{ - fmt_content, get_ing_at_user_tag_text, ing_star_tag_to_text, rm_ing_at_user_tag, IngSendFrom, -}; -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::result::IntoResult; -use crate::infra::str::StrExt; -use crate::infra::terminal::get_term_width; -use crate::infra::time::display_cnb_time; -use anyhow::Result; -use std::fmt::{Display, Write}; -use std::ops::Not; -use std::path::PathBuf; -use unicode_width::UnicodeWidthStr; - -#[inline] -pub fn fmt_err(e: &anyhow::Error) -> String { - format!("Err: {}", e) -} - -#[inline] -pub fn fmt_result(result: &Result) -> String { - match result { - Ok(t) => format!("Ok: {}", t), - Err(e) => fmt_err(e), - } -} - -pub fn login(cfg_path: &Result) -> String { - match cfg_path { - Ok(pb) => format!("PAT was saved in {:?}", pb), - Err(e) => fmt_err(e), - } -} - -pub fn logout(cfg_path: &Result) -> String { - match cfg_path { - Ok(pb) => format!("{:?} was successfully removed", pb), - Err(e) => fmt_err(e), - } -} - -pub fn user_info(info: &Result) -> Result { - let info = match info { - Ok(info) => info, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - write!(buf, "{}", info.display_name)?; - if info.is_vip { - write!(buf, " VIP")?; - } - writeln!(buf)?; - writeln!( - buf, - "{} Following {} Followers", - info.following_count, info.followers_count - )?; - writeln!(buf, "ID {}", info.blog_id)?; - writeln!(buf, "Joined {}", info.joined)?; - writeln!(buf, "Blog https://www.cnblogs.com/{}", info.blog_app)?; - } - buf.into_ok() -} - -// TODO: rm unnecessary line divider -pub fn list_ing( - time_style: &TimeStyle, - ing_with_comment_list: Result)>>, - align: bool, -) -> Result { - let mut ing_with_comment_list = match ing_with_comment_list { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - ing_with_comment_list.try_fold(String::new(), |mut buf, (ing, comment_list)| try { - { - let buf = &mut buf; - let create_time = display_cnb_time(&ing.create_time, time_style); - write!(buf, "{}", create_time)?; - - let send_from_mark = match ing.send_from { - IngSendFrom::Cli => Some("CLI"), - IngSendFrom::CellPhone => Some("Mobile"), - IngSendFrom::VsCode => Some("VSCode"), - IngSendFrom::Web => Some("Web"), - _ => None, - }; - if let Some(mark) = send_from_mark { - write!(buf, " {}", mark)?; - } - if ing.is_lucky { - let star_text = ing_star_tag_to_text(&ing.icons); - write!(buf, " {}★", star_text)?; - } - writeln!(buf, " # {}", ing.id)?; - let content = if align { - let user_name_width = ing.user_name.width_cjk(); - let left_width = get_term_width().saturating_sub(user_name_width + 3); - fmt_content(&ing.content) - .width_split(left_width) - .map_or_else( - || ing.content.clone(), - |lines| { - if comment_list.is_empty().not() { - lines.join("\n").replace( - '\n', - &format!("\n │{}", " ".repeat(user_name_width - 2)), - ) - } else { - lines.join("\n").replace( - '\n', - &format!("\n{}", " ".repeat(user_name_width + 3)), - ) - } - }, - ) - } else { - fmt_content(&ing.content) - }; - writeln!(buf, " {}: {}", ing.user_name, content)?; - - let len = comment_list.len(); - if len != 0 { - let max_i = len - 1; - let comment_list_buf: Result = comment_list.iter().enumerate().try_fold( - String::new(), - |mut buf, (i, entry)| try { - { - let buf = &mut buf; - if i != max_i { - write!(buf, " │ {}", entry.user_name)?; - } else { - write!(buf, " └ {}", entry.user_name)?; - } - let at_user = get_ing_at_user_tag_text(&entry.content); - if at_user.is_empty().not() { - write!(buf, " @{}", at_user)?; - } - let content = { - let content = rm_ing_at_user_tag(&entry.content); - fmt_content(&content) - }; - writeln!(buf, ": {}", content)?; - } - buf - }, - ); - write!(buf, "{}", comment_list_buf?)?; - } - - writeln!(buf)?; - }; - buf - }) -} - -pub fn show_post(entry: &Result) -> Result { - let entry = match entry { - Ok(entry) => entry, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - writeln!(buf, "{}\n", entry.title)?; - if let Some(body) = &entry.body { - writeln!(buf, "{}", body)?; - } - } - buf.into_ok() -} - -pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) -> Result { - let entry = match entry { - Ok(entry) => entry, - Err(e) => return fmt_err(e).into_ok(), - }; - - let mut buf = String::new(); - { - let buf = &mut buf; - writeln!(buf, "Title {}", entry.title)?; - { - write!(buf, "Status")?; - if entry.is_published { - write!(buf, " Published")?; - } else { - write!(buf, " Draft")?; - } - if entry.is_pinned { - write!(buf, " Pinned")?; - } - writeln!(buf)?; - }; - if let Some(body) = &entry.body { - let words_count = words_count::count(body).words; - writeln!(buf, "Words {}", words_count)?; - } - if let Some(tags) = &entry.tags { - if let Some(tags_text) = tags - .clone() - .into_iter() - .reduce(|acc, tag| format!("{}, {}", acc, tag)) - { - writeln!(buf, "Tags {}", tags_text)?; - } - } - let create_time = display_cnb_time(&entry.create_time, time_style); - writeln!(buf, "Create {}", create_time)?; - let modify_time = display_cnb_time(&entry.create_time, time_style); - writeln!(buf, "Modify {}", modify_time)?; - writeln!(buf, "Link https:{}", entry.url)?; - } - buf.into_ok() -} - -pub fn show_post_comment( - time_style: &TimeStyle, - comment_iter: Result>, -) -> Result { - let mut comment_iter = match comment_iter { - Ok(entry) => entry, - Err(e) => return fmt_err(&e).into_ok(), - }; - - comment_iter.try_fold(String::new(), |mut buf, comment| try { - { - let buf = &mut buf; - let create_time = display_cnb_time(&comment.create_time, time_style); - writeln!(buf, "{} {}F", create_time, comment.floor)?; - writeln!(buf, " {} {}", comment.user_name, comment.content)?; - } - buf - }) -} - -pub fn list_post( - result: Result<(impl ExactSizeIterator, usize)>, -) -> Result { - let (mut entry_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - entry_iter.try_fold( - format!("{}/{}\n", entry_iter.len(), total_count), - |mut buf, entry| try { - { - let buf = &mut buf; - write!(buf, "# {}", entry.id)?; - write!(buf, " {}", entry.title)?; - if entry.is_published { - write!(buf, " Pub")?; - } else { - write!(buf, " Dft")?; - } - if entry.is_pinned { - write!(buf, " Pin")?; - } - writeln!(buf)?; - } - buf - }, - ) -} - -pub fn search_post( - result: Result<(impl ExactSizeIterator, usize)>, -) -> Result { - let (mut id_iter, total_count) = match result { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - id_iter.try_fold( - format!("{}/{}\n", id_iter.len(), total_count), - |mut buf, id| try { - writeln!(&mut buf, "# {}", id)?; - buf - }, - ) -} - -pub fn list_news( - time_style: &TimeStyle, - news_iter: Result>, -) -> Result { - let news_iter = match news_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - news_iter - .map(|news| try { - let mut buf = String::new(); - { - let buf = &mut buf; - let create_time = display_cnb_time(&news.create_time, time_style); - let url = format!("https://news.cnblogs.com/n/{}", news.id); - writeln!(buf, "{} {}", create_time, url)?; - writeln!(buf, " {}", news.title)?; - - let summary = { - let summary = format!("{}...", news.summary); - summary.width_split(get_term_width() - 4).map_or_else( - || summary.clone(), - |vec| { - vec.into_iter() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - }, - ) - }; - writeln!(buf, "{}", summary)?; - } - buf - }) - .try_fold(String::new(), |mut acc, buf: Result| try { - write!(&mut acc, "\n{}", buf?)?; - acc - }) -} - -pub fn list_fav( - time_style: &TimeStyle, - fav_iter: Result>, -) -> Result { - let fav_iter = match fav_iter { - Ok(o) => o, - Err(e) => return fmt_err(&e).into_ok(), - }; - - fav_iter - .map(|fav| try { - let mut buf = String::new(); - { - let buf = &mut buf; - let create_time = display_cnb_time(&fav.create_time, time_style); - writeln!(buf, "{} {}", create_time, fav.url)?; - writeln!(buf, " {}", fav.title)?; - - let summary = { - fav.summary.width_split(get_term_width() - 4).map_or_else( - || fav.summary.clone(), - |vec| { - vec.into_iter() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - }, - ) - }; - if summary.is_empty().not() { - writeln!(buf, "{}", summary)?; - } - } - buf - }) - .try_fold(String::new(), |mut acc, buf: Result| try { - write!(&mut acc, "\n{}", buf?)?; - acc - }) -} diff --git a/src/display/normal/fav.rs b/src/display/normal/fav.rs new file mode 100644 index 0000000..e6ea027 --- /dev/null +++ b/src/display/normal/fav.rs @@ -0,0 +1,51 @@ +use crate::api::fav::get_list::FavEntry; +use crate::args::TimeStyle; +use crate::display::normal::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use std::fmt::Write; +use std::ops::Not; + +pub fn list_fav( + time_style: &TimeStyle, + fav_iter: Result>, +) -> Result { + let fav_iter = match fav_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + fav_iter + .map(|fav| try { + let mut buf = String::new(); + { + let buf = &mut buf; + let create_time = display_cnb_time(&fav.create_time, time_style); + writeln!(buf, "{} {}", create_time, fav.url)?; + writeln!(buf, " {}", fav.title)?; + + let summary = { + fav.summary.width_split(get_term_width() - 4).map_or_else( + || fav.summary.clone(), + |vec| { + vec.into_iter() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + }, + ) + }; + if summary.is_empty().not() { + writeln!(buf, "{}", summary)?; + } + } + buf + }) + .try_fold(String::new(), |mut acc, buf: Result| try { + write!(&mut acc, "\n{}", buf?)?; + acc + }) +} diff --git a/src/display/normal/ing.rs b/src/display/normal/ing.rs new file mode 100644 index 0000000..a1914cd --- /dev/null +++ b/src/display/normal/ing.rs @@ -0,0 +1,108 @@ +use crate::api::ing::get_comment_list::IngCommentEntry; +use crate::api::ing::get_list::IngEntry; +use crate::api::ing::{ + fmt_content, get_ing_at_user_tag_text, ing_star_tag_to_text, rm_ing_at_user_tag, IngSendFrom, +}; +use crate::args::TimeStyle; +use crate::display::normal::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use std::fmt::Write; +use std::ops::Not; +use unicode_width::UnicodeWidthStr; + +// TODO: rm unnecessary line divider +pub fn list_ing( + time_style: &TimeStyle, + ing_with_comment_list: Result)>>, + align: bool, +) -> Result { + let mut ing_with_comment_list = match ing_with_comment_list { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + ing_with_comment_list.try_fold(String::new(), |mut buf, (ing, comment_list)| try { + { + let buf = &mut buf; + let create_time = display_cnb_time(&ing.create_time, time_style); + write!(buf, "{}", create_time)?; + + let send_from_mark = match ing.send_from { + IngSendFrom::Cli => Some("CLI"), + IngSendFrom::CellPhone => Some("Mobile"), + IngSendFrom::VsCode => Some("VSCode"), + IngSendFrom::Web => Some("Web"), + _ => None, + }; + if let Some(mark) = send_from_mark { + write!(buf, " {}", mark)?; + } + if ing.is_lucky { + let star_text = ing_star_tag_to_text(&ing.icons); + write!(buf, " {}★", star_text)?; + } + writeln!(buf, " # {}", ing.id)?; + let content = if align { + let user_name_width = ing.user_name.width_cjk(); + let left_width = get_term_width().saturating_sub(user_name_width + 3); + fmt_content(&ing.content) + .width_split(left_width) + .map_or_else( + || ing.content.clone(), + |lines| { + if comment_list.is_empty().not() { + lines.join("\n").replace( + '\n', + &format!("\n │{}", " ".repeat(user_name_width - 2)), + ) + } else { + lines.join("\n").replace( + '\n', + &format!("\n{}", " ".repeat(user_name_width + 3)), + ) + } + }, + ) + } else { + fmt_content(&ing.content) + }; + writeln!(buf, " {}: {}", ing.user_name, content)?; + + let len = comment_list.len(); + if len != 0 { + let max_i = len - 1; + let comment_list_buf: Result = comment_list.iter().enumerate().try_fold( + String::new(), + |mut buf, (i, entry)| try { + { + let buf = &mut buf; + if i != max_i { + write!(buf, " │ {}", entry.user_name)?; + } else { + write!(buf, " └ {}", entry.user_name)?; + } + let at_user = get_ing_at_user_tag_text(&entry.content); + if at_user.is_empty().not() { + write!(buf, " @{}", at_user)?; + } + let content = { + let content = rm_ing_at_user_tag(&entry.content); + fmt_content(&content) + }; + writeln!(buf, ": {}", content)?; + } + buf + }, + ); + write!(buf, "{}", comment_list_buf?)?; + } + + writeln!(buf)?; + }; + buf + }) +} diff --git a/src/display/normal/mod.rs b/src/display/normal/mod.rs new file mode 100644 index 0000000..2c941b9 --- /dev/null +++ b/src/display/normal/mod.rs @@ -0,0 +1,21 @@ +pub mod fav; +pub mod ing; +pub mod news; +pub mod post; +pub mod user; + +use anyhow::Result; +use std::fmt::Display; + +#[inline] +pub fn fmt_err(e: &anyhow::Error) -> String { + format!("Err: {}", e) +} + +#[inline] +pub fn fmt_result(result: &Result) -> String { + match result { + Ok(t) => format!("Ok: {}", t), + Err(e) => fmt_err(e), + } +} diff --git a/src/display/normal/news.rs b/src/display/normal/news.rs new file mode 100644 index 0000000..0fd5923 --- /dev/null +++ b/src/display/normal/news.rs @@ -0,0 +1,50 @@ +use crate::api::news::get_list::NewsEntry; +use crate::args::TimeStyle; +use crate::display::normal::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::str::StrExt; +use crate::infra::terminal::get_term_width; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use std::fmt::Write; + +pub fn list_news( + time_style: &TimeStyle, + news_iter: Result>, +) -> Result { + let news_iter = match news_iter { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + news_iter + .map(|news| try { + let mut buf = String::new(); + { + let buf = &mut buf; + let create_time = display_cnb_time(&news.create_time, time_style); + let url = format!("https://news.cnblogs.com/n/{}", news.id); + writeln!(buf, "{} {}", create_time, url)?; + writeln!(buf, " {}", news.title)?; + + let summary = { + let summary = format!("{}...", news.summary); + summary.width_split(get_term_width() - 4).map_or_else( + || summary.clone(), + |vec| { + vec.into_iter() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + }, + ) + }; + writeln!(buf, "{}", summary)?; + } + buf + }) + .try_fold(String::new(), |mut acc, buf: Result| try { + write!(&mut acc, "\n{}", buf?)?; + acc + }) +} diff --git a/src/display/normal/post.rs b/src/display/normal/post.rs new file mode 100644 index 0000000..8c0e751 --- /dev/null +++ b/src/display/normal/post.rs @@ -0,0 +1,136 @@ +use crate::api::post::get_comment_list::PostCommentEntry; +use crate::api::post::get_one::PostEntry; +use crate::args::TimeStyle; +use crate::display::normal::fmt_err; +use crate::infra::result::IntoResult; +use crate::infra::time::display_cnb_time; +use anyhow::Result; +use std::fmt::Write; + +pub fn list_post( + result: Result<(impl ExactSizeIterator, usize)>, +) -> Result { + let (mut entry_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + entry_iter.try_fold( + format!("{}/{}\n", entry_iter.len(), total_count), + |mut buf, entry| try { + { + let buf = &mut buf; + write!(buf, "# {}", entry.id)?; + write!(buf, " {}", entry.title)?; + if entry.is_published { + write!(buf, " Pub")?; + } else { + write!(buf, " Dft")?; + } + if entry.is_pinned { + write!(buf, " Pin")?; + } + writeln!(buf)?; + } + buf + }, + ) +} + +pub fn show_post(entry: &Result) -> Result { + let entry = match entry { + Ok(entry) => entry, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + writeln!(buf, "{}\n", entry.title)?; + if let Some(body) = &entry.body { + writeln!(buf, "{}", body)?; + } + } + buf.into_ok() +} + +pub fn show_post_meta(time_style: &TimeStyle, entry: &Result) -> Result { + let entry = match entry { + Ok(entry) => entry, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + writeln!(buf, "Title {}", entry.title)?; + { + write!(buf, "Status")?; + if entry.is_published { + write!(buf, " Published")?; + } else { + write!(buf, " Draft")?; + } + if entry.is_pinned { + write!(buf, " Pinned")?; + } + writeln!(buf)?; + }; + if let Some(body) = &entry.body { + let words_count = words_count::count(body).words; + writeln!(buf, "Words {}", words_count)?; + } + if let Some(tags) = &entry.tags { + if let Some(tags_text) = tags + .clone() + .into_iter() + .reduce(|acc, tag| format!("{}, {}", acc, tag)) + { + writeln!(buf, "Tags {}", tags_text)?; + } + } + let create_time = display_cnb_time(&entry.create_time, time_style); + writeln!(buf, "Create {}", create_time)?; + let modify_time = display_cnb_time(&entry.create_time, time_style); + writeln!(buf, "Modify {}", modify_time)?; + writeln!(buf, "Link https:{}", entry.url)?; + } + buf.into_ok() +} + +pub fn show_post_comment( + time_style: &TimeStyle, + comment_iter: Result>, +) -> Result { + let mut comment_iter = match comment_iter { + Ok(entry) => entry, + Err(e) => return fmt_err(&e).into_ok(), + }; + + comment_iter.try_fold(String::new(), |mut buf, comment| try { + { + let buf = &mut buf; + let create_time = display_cnb_time(&comment.create_time, time_style); + writeln!(buf, "{} {}F", create_time, comment.floor)?; + writeln!(buf, " {} {}", comment.user_name, comment.content)?; + } + buf + }) +} + +pub fn search_post( + result: Result<(impl ExactSizeIterator, usize)>, +) -> Result { + let (mut id_iter, total_count) = match result { + Ok(o) => o, + Err(e) => return fmt_err(&e).into_ok(), + }; + + id_iter.try_fold( + format!("{}/{}\n", id_iter.len(), total_count), + |mut buf, id| try { + writeln!(&mut buf, "# {}", id)?; + buf + }, + ) +} diff --git a/src/display/normal/user.rs b/src/display/normal/user.rs new file mode 100644 index 0000000..7af503d --- /dev/null +++ b/src/display/normal/user.rs @@ -0,0 +1,46 @@ +use crate::api::user::info::UserInfo; +use crate::display::normal::fmt_err; +use crate::infra::result::IntoResult; +use anyhow::Result; +use std::fmt::Write; +use std::path::PathBuf; + +pub fn login(cfg_path: &Result) -> String { + match cfg_path { + Ok(pb) => format!("PAT was saved in {:?}", pb), + Err(e) => fmt_err(e), + } +} + +pub fn logout(cfg_path: &Result) -> String { + match cfg_path { + Ok(pb) => format!("{:?} was successfully removed", pb), + Err(e) => fmt_err(e), + } +} + +pub fn user_info(info: &Result) -> Result { + let info = match info { + Ok(info) => info, + Err(e) => return fmt_err(e).into_ok(), + }; + + let mut buf = String::new(); + { + let buf = &mut buf; + write!(buf, "{}", info.display_name)?; + if info.is_vip { + write!(buf, " VIP")?; + } + writeln!(buf)?; + writeln!( + buf, + "{} Following {} Followers", + info.following_count, info.followers_count + )?; + writeln!(buf, "ID {}", info.blog_id)?; + writeln!(buf, "Joined {}", info.joined)?; + writeln!(buf, "Blog https://www.cnblogs.com/{}", info.blog_app)?; + } + buf.into_ok() +}