feat: unify CLI and API with DataService trait

- Add DataService trait with streaming support for save/get operations
- Implement SyncDataService for CLI and AsyncDataService for API
- Add missing API endpoints: DELETE /api/item/{id}, GET /api/item/{id}/info, GET /api/diff
- Add GET /api/plugins/status endpoint
- Preserve stdin/stdout streaming performance via Read trait
This commit is contained in:
2026-03-10 22:31:31 -03:00
parent fb4c1a2b11
commit e8ea42506e
8 changed files with 742 additions and 2 deletions

View File

@@ -0,0 +1,148 @@
use crate::common::status::StatusInfo;
use crate::config::Settings;
use crate::db::Item;
use crate::db::Meta;
use crate::services::data_service::DataService;
use crate::services::error::CoreError;
use crate::services::types::{ItemWithContent, ItemWithMeta};
use clap::Command;
use rusqlite::Connection;
use std::collections::HashMap;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct AsyncDataService {
data_path: PathBuf,
settings: Arc<Settings>,
db: Arc<Mutex<Connection>>,
}
impl AsyncDataService {
pub fn new(data_path: PathBuf, settings: Arc<Settings>, db: Arc<Mutex<Connection>>) -> Self {
Self {
data_path,
settings,
db,
}
}
pub fn data_path(&self) -> &PathBuf {
&self.data_path
}
pub fn settings(&self) -> Arc<Settings> {
self.settings.clone()
}
pub fn db(&self) -> Arc<Mutex<Connection>> {
self.db.clone()
}
}
impl DataService for AsyncDataService {
type Error = CoreError;
fn save<R: Read>(
&self,
content: R,
cmd: &mut Command,
settings: &Settings,
tags: Vec<String>,
conn: &mut Connection,
) -> Result<Item, Self::Error> {
let sync_service =
crate::services::SyncDataService::new(self.data_path.clone(), settings.clone());
sync_service.save(content, cmd, settings, tags, conn)
}
fn get(&self, conn: &mut Connection, id: i64) -> Result<ItemWithMeta, Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.get(conn, id)
}
fn get_content(
&self,
conn: &mut Connection,
id: i64,
) -> Result<(Box<dyn Read + Send>, ItemWithMeta), Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.get_content(conn, id)
}
fn list(
&self,
conn: &mut Connection,
tags: Vec<String>,
meta: HashMap<String, String>,
) -> Result<Vec<ItemWithMeta>, Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.list(conn, tags, meta)
}
fn delete(&self, conn: &mut Connection, id: i64) -> Result<Item, Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.delete(conn, id)
}
fn find_item(
&self,
conn: &mut Connection,
ids: Vec<i64>,
tags: Vec<String>,
meta: HashMap<String, String>,
) -> Result<ItemWithMeta, Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.find_item(conn, ids, tags, meta)
}
fn get_items(
&self,
conn: &mut Connection,
ids: &[i64],
tags: &[String],
meta: &HashMap<String, String>,
) -> Result<Vec<ItemWithMeta>, Self::Error> {
let sync_service = crate::services::SyncDataService::new(
self.data_path.clone(),
self.settings.as_ref().clone(),
);
sync_service.get_items(conn, ids, tags, meta)
}
fn generate_status(
&self,
_cmd: &Command,
settings: &Settings,
data_path: &PathBuf,
db_path: &PathBuf,
) -> Result<StatusInfo, Self::Error> {
let mut cmd_mut = Command::new("keep");
let sync_service =
crate::services::SyncDataService::new(self.data_path.clone(), settings.clone());
Ok(
sync_service.generate_status(
&mut cmd_mut,
settings,
data_path.clone(),
db_path.clone(),
),
)
}
}