From ed5d7c70759000ebc7f7a6e195eb723b1ad195a9 Mon Sep 17 00:00:00 2001 From: SidneyZhang Date: Thu, 16 Apr 2026 17:15:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(cli,client):=20=E5=B0=86=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=94=B9=E4=B8=BA=20pageToken/pageSize=20?= =?UTF-8?q?=E5=B9=B6=E6=94=AF=E6=8C=81=E5=85=A8=E9=87=8F=E6=8B=89=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cli.rs | 8 +++--- src/client.rs | 70 ++++++++++++++++++++++++++++++++++++++++++--------- src/main.rs | 6 ++--- src/models.rs | 6 +++++ 4 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 982da18..debe0c9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -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, } #[derive(Parser, Debug)] diff --git a/src/client.rs b/src/client.rs index 3127c5c..272da6a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -22,16 +22,11 @@ impl MemosClient { pub async fn create_memo(&self, content: &str, visibility: Option<&str>) -> Result { 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, offset: Option) -> Result> { + pub async fn list_memos(&self, page_size: Option, page_token: Option<&str>) -> Result> { 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, page_token: Option<&str>) -> Result { + 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> { + let mut all_memos = Vec::new(); + let mut page_token: Option = 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) + } } diff --git a/src/main.rs b/src/main.rs index d419dbb..91899da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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> { 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; diff --git a/src/models.rs b/src/models.rs index 29bfb98..8f57508 100644 --- a/src/models.rs +++ b/src/models.rs @@ -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, } +fn default_state() -> String { + "STATE_UNSPECIFIED".to_string() +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Memo { #[serde(skip)]