feat(cli,client): 将分页参数改为 pageToken/pageSize 并支持全量拉取

This commit is contained in:
2026-04-16 17:15:52 +08:00
parent b67752bcc2
commit ed5d7c7075
4 changed files with 71 additions and 19 deletions

View File

@@ -35,11 +35,11 @@ pub struct CreateCommand {
#[derive(Parser, Debug)]
pub struct ListCommand {
#[arg(short, long, default_value = "20", help = "Number of memos to list")]
pub limit: i32,
#[arg(short, long, default_value = "50", help = "Number of memos to list per page")]
pub page_size: i32,
#[arg(short, long, default_value = "0", help = "Offset for pagination")]
pub offset: i32,
#[arg(short, long, help = "Page token for pagination")]
pub page_token: Option<String>,
}
#[derive(Parser, Debug)]

View File

@@ -22,16 +22,11 @@ impl MemosClient {
pub async fn create_memo(&self, content: &str, visibility: Option<&str>) -> Result<Memo> {
let url = format!("{}/api/v1/memos", self.base_url);
let visibility_str = visibility.map(|v| {
if v.starts_with("VISIBILITY_") {
v.to_string()
} else {
format!("VISIBILITY_{}", v.to_uppercase())
}
});
let visibility_str = visibility.map(|v| v.to_string());
let request = CreateMemoRequest {
content: content.to_string(),
state: "STATE_UNSPECIFIED".to_string(),
visibility: visibility_str,
};
@@ -55,7 +50,7 @@ impl MemosClient {
Ok(memo)
}
pub async fn list_memos(&self, limit: Option<i32>, offset: Option<i32>) -> Result<Vec<Memo>> {
pub async fn list_memos(&self, page_size: Option<i32>, page_token: Option<&str>) -> Result<Vec<Memo>> {
let url = format!("{}/api/v1/memos", self.base_url);
let mut request = self
@@ -63,11 +58,11 @@ impl MemosClient {
.get(&url)
.header("Authorization", format!("Bearer {}", self.user_token));
if let Some(l) = limit {
request = request.query(&[("limit", l.to_string())]);
if let Some(ps) = page_size {
request = request.query(&[("pageSize", ps.to_string())]);
}
if let Some(o) = offset {
request = request.query(&[("offset", o.to_string())]);
if let Some(token) = page_token {
request = request.query(&[("pageToken", token)]);
}
let response = request.send().await?;
@@ -81,4 +76,55 @@ impl MemosClient {
let result: ListMemosResponse = response.json().await?;
Ok(result.memos)
}
pub async fn list_memos_with_token(&self, page_size: Option<i32>, page_token: Option<&str>) -> Result<ListMemosResponse> {
let url = format!("{}/api/v1/memos", self.base_url);
let mut request = self
.client
.get(&url)
.header("Authorization", format!("Bearer {}", self.user_token));
if let Some(ps) = page_size {
request = request.query(&[("pageSize", ps.to_string())]);
}
if let Some(token) = page_token {
request = request.query(&[("pageToken", token)]);
}
let response = request.send().await?;
if !response.status().is_success() {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
anyhow::bail!("Failed to list memos: {} - {}", status, error_text);
}
let result: ListMemosResponse = response.json().await?;
Ok(result)
}
pub async fn list_all_memos(&self) -> Result<Vec<Memo>> {
let mut all_memos = Vec::new();
let mut page_token: Option<String> = None;
let page_size = 100;
loop {
let response = self.list_memos_with_token(Some(page_size), page_token.as_deref()).await?;
let memos = response.memos;
if memos.is_empty() {
break;
}
all_memos.extend(memos);
page_token = response.next_page_token;
if page_token.is_none() || page_token.as_ref().map_or(true, |t| t.is_empty()) {
break;
}
}
Ok(all_memos)
}
}

View File

@@ -71,7 +71,7 @@ async fn run_cli_command(command: Command) -> Result<()> {
}
}
}
Command::List(cmd) => match client.list_memos(Some(cmd.limit), Some(cmd.offset)).await {
Command::List(cmd) => match client.list_memos(Some(cmd.page_size), cmd.page_token.as_deref()).await {
Ok(memos) => {
if memos.is_empty() {
println!("No memos found.");
@@ -341,7 +341,7 @@ async fn run_tui_mode() -> Result<()> {
async fn load_memos(app: &App) -> Result<Vec<Memo>> {
let client = app.get_client().await?;
let memos = client.list_memos(None, None).await?;
let memos = client.list_all_memos().await?;
Ok(memos)
}
@@ -457,7 +457,7 @@ async fn handle_list_memos_input(app: &mut App, key: &KeyEvent) -> Result<()> {
match key.code {
KeyCode::Char('r') | KeyCode::Char('R') => {
let client = app.get_client().await?;
match client.list_memos(None, None).await {
match client.list_all_memos().await {
Ok(memos) => {
app.memos = memos;
app.current_page = 0;

View File

@@ -3,10 +3,16 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateMemoRequest {
pub content: String,
#[serde(default = "default_state")]
pub state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub visibility: Option<String>,
}
fn default_state() -> String {
"STATE_UNSPECIFIED".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memo {
#[serde(skip)]