Skip to content

Commit 328dafb

Browse files
authored
release v0.3.0 (#10)
* feat: add follower list command * style: format follower list command * feat: add following list command
1 parent f2b842b commit 328dafb

7 files changed

Lines changed: 152 additions & 4 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "cnblogs_lib"
33
# WRN: Version will be updated by CI while create a tag, NERVER change this.
4-
version = "0.0.0-dev"
4+
version = "0.3.0"
55
edition = "2024"
66
description = "Cnblogs' command line tool"
77
license = "MIT"

src/api/urls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pub const OPENAPI: &str = "https://api.cnblogs.com/api";
55
pub const OAUTH: &str = "https://oauth.cnblogs.com";
66

77
pub const USER: &str = formatcp!("{}/users", OPENAPI);
8+
pub const USER_FOLLOWERS: &str = formatcp!("{}/followers", USER);
9+
pub const USER_FOLLOWING: &str = formatcp!("{}/following", USER);
810
pub const STATUS: &str = formatcp!("{}/statuses/", OPENAPI);
911
pub const COMMENTS_PATH: &str = "comments";
1012

src/api/user.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use anyhow::Result;
22
use reqwest::{Client, Response};
33

4-
use crate::{api::urls, models::user::UserInfo, tools::IntoAnyhowResult};
4+
use crate::{
5+
api::urls,
6+
models::user::{UserFollow, UserInfo},
7+
tools::IntoAnyhowResult,
8+
};
59

610
pub async fn raw_user_info(c: &Client) -> Result<Response> {
711
c.get(urls::USER).send().await.into_anyhow_result()
@@ -10,3 +14,57 @@ pub async fn raw_user_info(c: &Client) -> Result<Response> {
1014
pub async fn user_info(c: &Client) -> Result<UserInfo> {
1115
raw_user_info(c).await?.json().await.into_anyhow_result()
1216
}
17+
18+
/// 获取用户粉丝列表的原始响应
19+
/// page - 分页参数,包含 pageIndex 和 pageSize
20+
pub async fn raw_user_followers(
21+
c: &Client,
22+
page: impl serde::Serialize + Send + Sync,
23+
) -> Result<Response> {
24+
c.get(urls::USER_FOLLOWERS)
25+
.query(&page)
26+
.send()
27+
.await
28+
.into_anyhow_result()
29+
}
30+
31+
/// 获取用户粉丝列表
32+
///
33+
/// page - 分页参数,包含 pageIndex 和 pageSize
34+
pub async fn user_followers(
35+
c: &Client,
36+
page: impl serde::Serialize + Send + Sync,
37+
) -> Result<UserFollow> {
38+
raw_user_followers(c, page)
39+
.await?
40+
.json()
41+
.await
42+
.into_anyhow_result()
43+
}
44+
45+
/// 获取用户粉丝列表的原始响应
46+
/// page - 分页参数,包含 pageIndex 和 pageSize
47+
pub async fn raw_user_following(
48+
c: &Client,
49+
page: impl serde::Serialize + Send + Sync,
50+
) -> Result<Response> {
51+
c.get(urls::USER_FOLLOWING)
52+
.query(&page)
53+
.send()
54+
.await
55+
.into_anyhow_result()
56+
}
57+
58+
/// 获取用户粉丝列表
59+
///
60+
/// page - 分页参数,包含 pageIndex 和 pageSize
61+
pub async fn user_following(
62+
c: &Client,
63+
page: impl serde::Serialize + Send + Sync,
64+
) -> Result<UserFollow> {
65+
raw_user_following(c, page)
66+
.await?
67+
.json()
68+
.await
69+
.into_anyhow_result()
70+
}

src/commands/user.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ pub struct UserCommand {
1313
/// 提供通过access token登录,状态查询,退出,显示当前token功能
1414
#[derive(Debug, Subcommand)]
1515
pub enum UserAction {
16+
/// 查看用户粉丝列表
17+
Follower(FollowArg),
18+
/// 查看用户关注列表
19+
Following(FollowArg),
1620
/// 用户登录,需提供access token。
1721
Login {
1822
#[clap(value_parser = NonEmptyStringValueParser::new())]
@@ -25,3 +29,16 @@ pub enum UserAction {
2529
/// 显示当前登录token
2630
Token,
2731
}
32+
33+
#[derive(serde::Serialize, Debug, Args)]
34+
pub struct FollowArg {
35+
/// 分页页码(从1开始)
36+
#[arg(long = "page-index", default_value_t = 1)]
37+
#[serde(rename = "pageIndex")]
38+
pub page_index: u64,
39+
40+
/// 每页显示的条数,默认20
41+
#[arg(long = "page-size", default_value_t = 20)]
42+
#[serde(rename = "pageSize")]
43+
pub page_size: u64,
44+
}

src/logic/user.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ use owo_colors::OwoColorize;
77
use reqwest::header::{AUTHORIZATION, HeaderMap};
88
use reqwest::{ClientBuilder, StatusCode};
99

10-
use crate::commands::user::{UserAction, UserCommand};
10+
use crate::commands::user::{FollowArg, UserAction, UserCommand};
1111
use crate::context::Context;
1212
use crate::context::config::Cache;
1313
use crate::tools::http::IntoNoParseResult;
1414
use crate::{api, models};
1515

1616
pub async fn endpoint(cmd: UserCommand, ctx: &mut Context) -> anyhow::Result<()> {
1717
match cmd.commands {
18+
UserAction::Follower(arg) => handle_followers(ctx, arg).await,
19+
UserAction::Following(arg) => handle_following(ctx, arg).await,
1820
UserAction::Login { token } => handle_login(token, ctx).await,
1921
UserAction::Logout => handle_logout(ctx),
2022
UserAction::Status => user_info(ctx).await,
@@ -68,3 +70,37 @@ async fn user_info(ctx: &mut Context) -> Result<()> {
6870
fn handle_logout(ctx: &Context) -> Result<()> {
6971
ctx.clean()
7072
}
73+
74+
async fn handle_followers(ctx: &mut Context, arg: FollowArg) -> Result<()> {
75+
let followers = api::user::user_followers(&ctx.client, &arg).await?;
76+
77+
ctx.terminal.writeln(
78+
format!(
79+
"{} 人关注了您, 当前{}页,每页数量{}",
80+
followers.total_count, arg.page_index, arg.page_size
81+
)
82+
.bright_green(),
83+
)?;
84+
85+
followers
86+
.items
87+
.iter()
88+
.for_each(|f| ctx.terminal.writeln(f.as_format()).unwrap());
89+
Ok(())
90+
}
91+
92+
async fn handle_following(ctx: &mut Context, arg: FollowArg) -> Result<()> {
93+
let following = api::user::user_following(&ctx.client, &arg).await?;
94+
ctx.terminal.writeln(
95+
format!(
96+
"您关注了 {} 人, 当前{}页,每页数量{}",
97+
following.total_count, arg.page_index, arg.page_size
98+
)
99+
.bright_green(),
100+
)?;
101+
following
102+
.items
103+
.iter()
104+
.for_each(|f| ctx.terminal.writeln(f.as_format()).unwrap());
105+
Ok(())
106+
}

src/models/user.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,38 @@ impl UserInfo {
4343
info.join("\n")
4444
}
4545
}
46+
47+
#[derive(Clone, Debug, Deserialize, Serialize)]
48+
#[serde(rename_all = "PascalCase")]
49+
pub struct FollowInfo {
50+
pub alias: String,
51+
pub space_user_id: u64,
52+
pub display_name: String,
53+
pub blog_app: Option<String>,
54+
}
55+
56+
impl FollowInfo {
57+
pub fn as_format(&self) -> String {
58+
format!(
59+
"{name} [#{id}] [{blog}]",
60+
name = self.display_name.bright_blue(),
61+
id = self.space_user_id.bright_green(),
62+
blog = self
63+
.blog_app
64+
.as_ref()
65+
.map_or("无博客".red().to_string(), |app| format!(
66+
"https://www.cnblogs.com/{}",
67+
app
68+
)
69+
.blue()
70+
.to_string())
71+
)
72+
}
73+
}
74+
75+
#[derive(Clone, Debug, Deserialize, Serialize)]
76+
#[serde(rename_all = "PascalCase")]
77+
pub struct UserFollow {
78+
pub items: Vec<FollowInfo>,
79+
pub total_count: u64,
80+
}

0 commit comments

Comments
 (0)