Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ runs:
- name: Build release
if: ${{ inputs.release == 'true' }}
shell: bash
run: cargo build --target ${{ inputs.target }} -r
run: cargo build --release --target ${{ inputs.target }} -r
7 changes: 1 addition & 6 deletions .github/workflows/build-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
override: true
toolchain: nightly-2023-09-06
components: rustfmt, clippy
uses: dsherret/rust-toolchain-file@v1

- name: Show Rust toolchain version
shell: bash
Expand Down
10 changes: 2 additions & 8 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
ref: ${{ github.ref }} # github.ref 变量将自动填充为触发事件的分支或标签名

- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
override: true
toolchain: nightly-2023-09-06
components: rustfmt, clippy
uses: dsherret/rust-toolchain-file@v1

- name: Setup musl-tools
if: matrix.targets.target == 'x86_64-unknown-linux-musl'
Expand Down Expand Up @@ -99,4 +94,3 @@ jobs:
uses: softprops/action-gh-release@v1
with:
files: ${{ steps.gen-name.outputs.NAME }}.zip

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "cnb"
name = "cnblogs_lib"
# WRN: Version will be updated by CI while create a tag, NERVER change this.
version = "0.0.0-dev"
edition = "2021"
Expand Down Expand Up @@ -47,3 +47,7 @@ colored = "2.0.4"
terminal_size = "0.2.6"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bin]]
name = "cnb"
path = "src/bin/cnb.rs"
52 changes: 52 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Cnblogs 命令行工具

[![Build / Release](https://github.com/cnblogs/cli/actions/workflows/build-release.yml/badge.svg)](https://github.com/cnblogs/cli/actions/workflows/build-release.yml)
[![Build / Development](https://github.com/cnblogs/cli/actions/workflows/build-dev.yml/badge.svg)](https://github.com/cnblogs/cli/actions/workflows/build-dev.yml)

从 CLI 访问 cnblogs。

## Cnbogs Cli 设计

从Cnblogs的[OpenAPI](https://api.cnblogs.com/help)来说,API主要有以下几类:

1. Token: 认证
2. Users: 仅提供当前登录用户信息
3. Blogs: 博客的CURD及其评论的查看和增加,
4. Marks: 收藏的CURD
5. News: 新闻的查询,新闻评论的CURD
6. Statuses: 闪存CURD。
7. Questions: 问题相关操作
8. Edu: 班级相关
9. Articles: 知识库的查找。
10. Zzk: 找找看

### cli的使用

目前cli的使用如下:

```shell
# Check your post list
cnb post --list
# Check your post
cnb --id 114514 post --show
# Create and publish post
cnb post create --title 'Hello' --body 'world!' --publish
# Change your post body
cnb --id 114514 post update --body 'niconiconiconi'

# Show ing list
cnb ing list
# Publish ing
cnb ing --publish 'Hello world!'
# Comment to ing
cnb --id 114514 ing --comment 'Awesome!'

# Check your user infomation
cnb user --info
```

大体上使用如上的设计,支持子命令,相关操作的设计按照RESTFUL的思路设计实现,博客的相关操作设计如下:

```shell
cnb posts [comment] [list,create,query,delete,update] --[id/file/quertset] --[pagesize,pagecount]
```
5 changes: 5 additions & 0 deletions rust-fmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
max_width = 79 # 设置最大行宽为 100 个字符
tab_spaces = 4 # 设置缩进宽度为 4 个空格
edition = "2021" # 设置 Rust 版本(根据实际项目版本进行调整)
use_small_heuristics = "Max" # 设置换行策略
newline_style = "Auto" # 设置换行符风格,根据平台自动选择
5 changes: 3 additions & 2 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[toolchain]
profile = "default"
channel = "nightly-2023-12-27"
profile = "minimal"
channel = "nightly-2024-01-01"
components = [ "rustfmt", "clippy" ]
14 changes: 14 additions & 0 deletions src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! cnblogs 闪存接口模块
//!
//! 封装[cnblogs Api](https://api.cnblogs.com/Help#0aee001a01835c83a3277a500ffc9040)至以下模块中:
//!
//! - statuses: 闪存相关api。
//! - blogs: 博客相关
//! - news: 新闻相关
//! - questions: 问题相关
//! - edu: edu 相关
//! - user: 用户相关
//! - token: 认证相关
//! - marks: 收藏相关

pub mod statuses;
13 changes: 13 additions & 0 deletions src/apis/statuses/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! cnblogs 闪存接口模块
//!
//! 实现封装[cnblogs Api](https://api.cnblogs.com/Help#0aee001a01835c83a3277a500ffc9040)中的`Statuses`。
//!
//! - 获取最新一条闪存内容 https://api.cnblogs.com/api/statuses/recent
//! - 发布闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments
//! - 获取闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments
//! - 删除闪存评论 https://api.cnblogs.com/api/statuses/{statusId}/comments/{id}
//! - 发布闪存 https://api.cnblogs.com/api/statuses
//! - 删除闪存 https://api.cnblogs.com/api/statuses/{id}
//! - 根据类型获取闪存列表 https://api.cnblogs.com/api/statuses/@{type}?pageIndex={pageIndex}&pageSize={pageSize}&tag={tag}
//! - 根据Id获取闪存 https://api.cnblogs.com/api/statuses/{id}
//!
96 changes: 55 additions & 41 deletions src/main.rs → src/bin/cnb.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
#![feature(try_blocks)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(type_name_of_val)]
#![feature(iterator_try_collect)]
#![feature(iterator_try_reduce)]
#![warn(clippy::all, clippy::nursery, clippy::cargo_common_metadata)]

use crate::api::auth::session;
use crate::api::fav::Fav;
use crate::api::ing::Ing;
use crate::api::news::News;
use crate::api::post::Post;
use crate::api::user::User;
use crate::args::cmd::post::{CreateCmd, UpdateCmd};
use crate::args::parser::no_operation;
use crate::args::{parser, Args};
use crate::infra::fp::currying::eq;
use crate::infra::infer::infer;
use crate::infra::iter::{ExactSizeIteratorExt, IntoIteratorExt};
use crate::infra::option::OptionExt;
use crate::infra::result::WrapResult;
extern crate cnblogs_lib;

use anyhow::Result;
use clap::Parser;
use clap::{Command, CommandFactory};
use cnblogs_lib::api::auth::session;
use cnblogs_lib::api::fav::Fav;
use cnblogs_lib::api::ing::Ing;
use cnblogs_lib::api::news::News;
use cnblogs_lib::api::post::Post;
use cnblogs_lib::api::user::User;
use cnblogs_lib::args::cmd::post::{CreateCmd, UpdateCmd};
use cnblogs_lib::args::parser::no_operation;
use cnblogs_lib::args::{parser, Args};
use cnblogs_lib::display;
use cnblogs_lib::infra::fp::currying::eq;
use cnblogs_lib::infra::infer::infer;
use cnblogs_lib::infra::iter::{ExactSizeIteratorExt, IntoIteratorExt};
use cnblogs_lib::infra::option::OptionExt;
use cnblogs_lib::infra::result::WrapResult;
use colored::Colorize;
use std::env;

pub mod api;
pub mod args;
pub mod display;
pub mod infra;

fn show_non_printable_chars(text: String) -> String {
#[inline]
fn make_red(str: &str) -> String {
Expand All @@ -45,6 +42,7 @@ fn show_non_printable_chars(text: String) -> String {
.replace("\r\n", &make_red("␍␊\r\n"))
}

#[allow(clippy::missing_const_for_fn)]
fn panic_if_err<T>(result: &Result<T>) {
if let Err(e) = result {
panic!("{}", e)
Expand Down Expand Up @@ -87,19 +85,23 @@ async fn main() -> Result<()> {
display::user_info(style, &user_info)?
}
_ if let Some((skip, take, r#type, align)) = parser::ing::list_ing(&args) => {
let ing_with_comment_iter = infer::<Result<_, _>>(try {
let ing_api = Ing::new(pat?);
let ing_vec = ing_api.get_list(skip, take, &r#type).await?;
ing_vec.into_iter()
.map(|ing| async {
let result = ing_api.get_comment_list(ing.id).await;
result.map(|comment_vec| (ing, comment_vec))
})
.join_all()
.await
.into_iter()
.collect::<Result<Vec<_>>>()?
}).map(|vec| vec.into_iter().dyn_rev(rev));
let ing_with_comment_iter = infer::<Result<_, _>>(
try {
let ing_api = Ing::new(pat?);
let ing_vec = ing_api.get_list(skip, take, &r#type).await?;
ing_vec
.into_iter()
.map(|ing| async {
let result = ing_api.get_comment_list(ing.id).await;
result.map(|comment_vec| (ing, comment_vec))
})
.join_all()
.await
.into_iter()
.collect::<Result<Vec<_>>>()?
},
)
.map(|vec| vec.into_iter().dyn_rev(rev));
foe.then(|| panic_if_err(&ing_with_comment_iter));
display::list_ing(style, time_style, ing_with_comment_iter, align)?
}
Expand All @@ -113,7 +115,9 @@ async fn main() -> Result<()> {
}
_ if let Some((content, id)) = parser::ing::comment_ing(&args) => {
let content = try {
Ing::new(pat?).comment(id, content.clone(), None, None).await?;
Ing::new(pat?)
.comment(id, content.clone(), None, None)
.await?;
content
};
foe.then(|| panic_if_err(&content));
Expand All @@ -131,7 +135,8 @@ async fn main() -> Result<()> {
}
_ if let Some(id) = parser::post::show_post_comment(&args) => {
let comment_iter = Post::new(pat?)
.get_comment_list(id).await
.get_comment_list(id)
.await
.map(|vec| vec.into_iter().dyn_rev(rev));
foe.then(|| panic_if_err(&comment_iter));
display::show_post_comment(style, time_style, comment_iter)?
Expand Down Expand Up @@ -164,18 +169,28 @@ async fn main() -> Result<()> {
let result = Post::new(pat?)
.search_site(skip, take, kw)
.await
.map(|vec | vec.into_iter().dyn_rev(rev));
.map(|vec| vec.into_iter().dyn_rev(rev));
foe.then(|| panic_if_err(&result));
display::search_site_post(style, time_style, result)?
}
_ if let Some(create_cmd) = parser::post::create_post(&args) => {
let CreateCmd { title, body, publish } = create_cmd;
let CreateCmd {
title,
body,
publish,
..
} = create_cmd;
let id = Post::new(pat?).create(title, body, *publish).await;
foe.then(|| panic_if_err(&id));
display::create_post(style, &id)
}
_ if let Some((id, update_cmd)) = parser::post::update_post(&args) => {
let UpdateCmd { title, body, publish } = update_cmd;
let UpdateCmd {
title,
body,
publish,
..
} = update_cmd;
let id = Post::new(pat?).update(id, title, body, publish).await;
foe.then(|| panic_if_err(&id));
display::update_post(style, &id)
Expand All @@ -197,9 +212,8 @@ async fn main() -> Result<()> {
display::list_fav(style, time_style, fav_iter)?
}

_ if no_operation(&args) =>
infer::<Command>(Args::command()).render_help().to_string(),
_ => "Invalid usage, follow '--help' for more information".to_owned()
_ if no_operation(&args) => infer::<Command>(Args::command()).render_help().to_string(),
_ => "Invalid usage, follow '--help' for more information".to_owned(),
};

if global_opt.quiet {
Expand Down
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(try_blocks)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(iterator_try_collect)]
#![feature(iterator_try_reduce)]
#![warn(clippy::all, clippy::nursery, clippy::cargo_common_metadata)]

pub mod api;
pub mod apis;
pub mod args;
pub mod display;
pub mod infra;