docs: Add Rustdoc comments for various structs and functions

Co-authored-by: aider (openai/andrew/openrouter/sonoma-sky-alpha) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-10 14:23:12 -03:00
parent d219f557db
commit d44f3fbb5b
5 changed files with 292 additions and 154 deletions

View File

@@ -166,7 +166,7 @@ Private helpers (e.g., internal `fn` without `pub`) are not flagged, as they don
- `ListItem` struct: No doc. - `ListItem` struct: No doc.
- Helper functions (`apply_color`, `apply_attribute`, `show_list_structured`): No docs. - Helper functions (`apply_color`, `apply_attribute`, `show_list_structured`): No docs.
26. **src/filter_plugin/mod.rs** 26. **src/filter_plugin/mod.rs** [DONE]
- `FilterOption` struct: Partial. - `FilterOption` struct: Partial.
- `FilterPlugin` trait: Partial. - `FilterPlugin` trait: Partial.
- `FilterChain` struct and methods: Partial. - `FilterChain` struct and methods: Partial.
@@ -174,7 +174,7 @@ Private helpers (e.g., internal `fn` without `pub`) are not flagged, as they don
- `parse_filter_string()` function: Partial. - `parse_filter_string()` function: Partial.
- Helper functions (`create_filter_with_options`, etc.): No docs. - Helper functions (`create_filter_with_options`, etc.): No docs.
27. **src/services/meta_service.rs** 27. **src/services/meta_service.rs** [DONE]
- `MetaService` struct: No doc. - `MetaService` struct: No doc.
- Methods (`new`, `get_plugins`, `initialize_plugins`, etc.): Partial. - Methods (`new`, `get_plugins`, `initialize_plugins`, etc.): Partial.

View File

@@ -4,7 +4,15 @@ use serde::{Deserialize, Serialize};
use serde_yaml; use serde_yaml;
use crate::meta_plugin::MetaPlugin; use crate::meta_plugin::MetaPlugin;
/// Mode for generating a default configuration file.
///
/// This module creates a commented YAML template with default values for settings,
/// including list format, server config, compression, and meta plugins.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Default configuration structure for the generated template.
///
/// Includes core settings, list formatting, server options, compression, and meta plugins.
struct DefaultConfig { struct DefaultConfig {
dir: Option<String>, dir: Option<String>,
list_format: Vec<ColumnConfig>, list_format: Vec<ColumnConfig>,
@@ -18,6 +26,7 @@ struct DefaultConfig {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Configuration for a column in the list format.
struct ColumnConfig { struct ColumnConfig {
name: String, name: String,
label: Option<String>, label: Option<String>,
@@ -29,6 +38,7 @@ struct ColumnConfig {
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
/// Alignment options for table columns.
enum ColumnAlignment { enum ColumnAlignment {
#[default] #[default]
Left, Left,
@@ -36,6 +46,7 @@ enum ColumnAlignment {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Server configuration options.
struct ServerConfig { struct ServerConfig {
address: Option<String>, address: Option<String>,
port: Option<u16>, port: Option<u16>,
@@ -45,11 +56,13 @@ struct ServerConfig {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Configuration for the compression plugin.
struct CompressionPluginConfig { struct CompressionPluginConfig {
name: String, name: String,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Configuration for a meta plugin.
struct MetaPluginConfig { struct MetaPluginConfig {
name: String, name: String,
#[serde(default)] #[serde(default)]
@@ -58,126 +71,155 @@ struct MetaPluginConfig {
outputs: std::collections::HashMap<String, String>, outputs: std::collections::HashMap<String, String>,
} }
pub fn mode_generate_config(_cmd: &mut Command, _settings: &crate::config::Settings) -> Result<()> { /// Generates and prints a default commented YAML configuration template.
// Create instances of each meta plugin to get their default options and outputs ///
let text_plugin = crate::meta_plugin::text::TextMetaPlugin::new(None, None); /// Creates instances of available meta plugins to populate default options and outputs,
let cwd_plugin = crate::meta_plugin::cwd::CwdMetaPlugin::new(None, None); /// then serializes the config to YAML with all lines commented for easy editing.
let digest_plugin = crate::meta_plugin::digest::DigestMetaPlugin::new(None, None); ///
let hostname_plugin = crate::meta_plugin::hostname::HostnameMetaPlugin::new(None, None); /// # Arguments
#[cfg(feature = "magic")] ///
let magic_file_plugin = crate::meta_plugin::magic_file::MagicFileMetaPlugin::new(None, None); /// * `_cmd` - Unused Clap command reference.
let env_plugin = crate::meta_plugin::env::EnvMetaPlugin::new(None, None); /// * `_settings` - Unused settings reference.
///
/// # Returns
///
/// `Ok(())` on success.
///
/// # Examples
///
/// ```
/// mode_generate_config(&mut cmd, &settings)?;
/// ```
pub fn mode_generate_config(_cmd: &mut Command, _settings: &crate::config::Settings) -> Result<()> {
// Create instances of each meta plugin to get their default options and outputs
let text_plugin = crate::meta_plugin::text::TextMetaPlugin::new(None, None);
let cwd_plugin = crate::meta_plugin::cwd::CwdMetaPlugin::new(None, None);
let digest_plugin = crate::meta_plugin::digest::DigestMetaPlugin::new(None, None);
let hostname_plugin = crate::meta_plugin::hostname::HostnameMetaPlugin::new(None, None);
#[cfg(feature = "magic")]
let magic_file_plugin = crate::meta_plugin::magic_file::MagicFileMetaPlugin::new(None, None);
let env_plugin = crate::meta_plugin::env::EnvMetaPlugin::new(None, None);
// Create a default configuration // Create a default configuration
let default_config = DefaultConfig { let default_config = DefaultConfig {
dir: Some("~/.local/share/keep".to_string()), dir: Some("~/.local/share/keep".to_string()),
list_format: vec![ list_format: vec![
ColumnConfig { ColumnConfig {
name: "id".to_string(), name: "id".to_string(),
label: Some("Item".to_string()), label: Some("Item".to_string()),
align: ColumnAlignment::Right, align: ColumnAlignment::Right,
max_len: None, max_len: None,
}, },
ColumnConfig { ColumnConfig {
name: "time".to_string(), name: "time".to_string(),
label: Some("Time".to_string()), label: Some("Time".to_string()),
align: ColumnAlignment::Right, align: ColumnAlignment::Right,
max_len: None, max_len: None,
}, },
ColumnConfig { ColumnConfig {
name: "size".to_string(), name: "size".to_string(),
label: Some("Size".to_string()), label: Some("Size".to_string()),
align: ColumnAlignment::Right, align: ColumnAlignment::Right,
max_len: None, max_len: None,
}, },
ColumnConfig { ColumnConfig {
name: "meta:text_line_count".to_string(), name: "meta:text_line_count".to_string(),
label: Some("Lines".to_string()), label: Some("Lines".to_string()),
align: ColumnAlignment::Right, align: ColumnAlignment::Right,
max_len: None, max_len: None,
}, },
ColumnConfig { ColumnConfig {
name: "tags".to_string(), name: "tags".to_string(),
label: Some("Tags".to_string()), label: Some("Tags".to_string()),
align: ColumnAlignment::Left, align: ColumnAlignment::Left,
max_len: Some("40".to_string()), max_len: Some("40".to_string()),
}, },
ColumnConfig { ColumnConfig {
name: "meta:hostname_full".to_string(), name: "meta:hostname_full".to_string(),
label: Some("Hostname".to_string()), label: Some("Hostname".to_string()),
align: ColumnAlignment::Left, align: ColumnAlignment::Left,
max_len: Some("28".to_string()), max_len: Some("28".to_string()),
}, },
], ],
human_readable: false, human_readable: false,
output_format: Some("table".to_string()), output_format: Some("table".to_string()),
quiet: false, quiet: false,
force: false, force: false,
server: Some(ServerConfig { server: Some(ServerConfig {
address: Some("127.0.0.1".to_string()), address: Some("127.0.0.1".to_string()),
port: Some(8080), port: Some(8080),
password_file: None, password_file: None,
password: None, password: None,
password_hash: None, password_hash: None,
}), }),
compression_plugin: None, compression_plugin: None,
meta_plugins: Some(vec![ meta_plugins: Some(vec![
MetaPluginConfig { MetaPluginConfig {
name: "text".to_string(), name: "text".to_string(),
options: text_plugin.options().clone(), options: text_plugin.options().clone(),
outputs: convert_outputs_to_string_map(text_plugin.outputs()), outputs: convert_outputs_to_string_map(text_plugin.outputs()),
}, },
MetaPluginConfig { MetaPluginConfig {
name: "cwd".to_string(), name: "cwd".to_string(),
options: cwd_plugin.options().clone(), options: cwd_plugin.options().clone(),
outputs: convert_outputs_to_string_map(cwd_plugin.outputs()), outputs: convert_outputs_to_string_map(cwd_plugin.outputs()),
}, },
MetaPluginConfig { MetaPluginConfig {
name: "digest".to_string(), name: "digest".to_string(),
options: digest_plugin.options().clone(), options: digest_plugin.options().clone(),
outputs: convert_outputs_to_string_map(digest_plugin.outputs()), outputs: convert_outputs_to_string_map(digest_plugin.outputs()),
}, },
MetaPluginConfig { MetaPluginConfig {
name: "hostname".to_string(), name: "hostname".to_string(),
options: hostname_plugin.options().clone(), options: hostname_plugin.options().clone(),
outputs: convert_outputs_to_string_map(hostname_plugin.outputs()), outputs: convert_outputs_to_string_map(hostname_plugin.outputs()),
}, },
#[cfg(feature = "magic")] #[cfg(feature = "magic")]
MetaPluginConfig { MetaPluginConfig {
name: "magic_file".to_string(), name: "magic_file".to_string(),
options: magic_file_plugin.options().clone(), options: magic_file_plugin.options().clone(),
outputs: convert_outputs_to_string_map(magic_file_plugin.outputs()), outputs: convert_outputs_to_string_map(magic_file_plugin.outputs()),
}, },
MetaPluginConfig { MetaPluginConfig {
name: "env".to_string(), name: "env".to_string(),
options: env_plugin.options().clone(), options: env_plugin.options().clone(),
outputs: convert_outputs_to_string_map(env_plugin.outputs()), outputs: convert_outputs_to_string_map(env_plugin.outputs()),
}, },
]), ]),
}; };
// Serialize to YAML and comment out all lines // Serialize to YAML and comment out all lines
let yaml = serde_yaml::to_string(&default_config)?; let yaml = serde_yaml::to_string(&default_config)?;
// Comment out every line // Comment out every line
let commented_yaml = yaml let commented_yaml = yaml
.lines() .lines()
.map(|line| { .map(|line| {
if line.trim().is_empty() { if line.trim().is_empty() {
line.to_string() line.to_string()
} else { } else {
format!("# {}", line) format!("# {}", line)
} }
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
println!("{}", commented_yaml); println!("{}", commented_yaml);
Ok(()) Ok(())
} }
// Helper function to convert outputs from serde_yaml::Value to String /// Helper function to convert outputs from serde_yaml::Value to String.
///
/// Handles null (uses key), strings, and other values by serializing to YAML string.
///
/// # Arguments
///
/// * `outputs` - Reference to the outputs HashMap.
///
/// # Returns
///
/// A HashMap with string keys and values.
fn convert_outputs_to_string_map( fn convert_outputs_to_string_map(
outputs: &std::collections::HashMap<String, serde_yaml::Value>, outputs: &std::collections::HashMap<String, serde_yaml::Value>,
) -> std::collections::HashMap<String, String> { ) -> std::collections::HashMap<String, String> {

View File

@@ -3,6 +3,10 @@ pub mod tools;
pub use server::KeepMcpServer; pub use server::KeepMcpServer;
/// Module for handling MCP (Model Context Protocol) requests in the server.
///
/// Provides handlers for JSON-RPC style requests to interact with Keep's storage
/// via the API.
use axum::{ use axum::{
extract::State, extract::State,
http::StatusCode, http::StatusCode,
@@ -15,46 +19,69 @@ use serde_json::Value;
use crate::modes::server::common::AppState; use crate::modes::server::common::AppState;
use crate::modes::server::common::ApiResponse; use crate::modes::server::common::ApiResponse;
/// Request structure for MCP JSON-RPC calls.
///
/// # Fields
///
/// * `method` - The MCP method name (e.g., "save_item").
/// * `params` - Optional JSON parameters for the method.
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct McpRequest { pub struct McpRequest {
pub method: String, pub method: String,
pub params: Option<Value>, pub params: Option<Value>,
} }
pub async fn handle_mcp_request( /// Handles an MCP request via the Axum framework.
State(state): State<AppState>, ///
Json(request): Json<McpRequest>, /// Parses the JSON request, delegates to `KeepMcpServer`, and returns an API response.
) -> impl IntoResponse { /// Attempts to parse the result as JSON; falls back to string if invalid.
let mcp_server = KeepMcpServer::new(state); ///
/// # Arguments
///
/// * `State(state)` - The application state.
/// * `Json(request)` - The deserialized MCP request.
///
/// # Returns
///
/// An `IntoResponse` with status code and JSON API response.
///
/// # Errors
///
/// Returns 400 Bad Request on handler errors.
pub async fn handle_mcp_request(
State(state): State<AppState>,
Json(request): Json<McpRequest>,
) -> impl IntoResponse {
let mcp_server = KeepMcpServer::new(state);
match mcp_server.handle_request(&request.method, request.params).await { match mcp_server.handle_request(&request.method, request.params).await {
Ok(result) => { Ok(result) => {
match serde_json::from_str(&result) { match serde_json::from_str(&result) {
Ok(parsed_result) => { Ok(parsed_result) => {
let response = ApiResponse { let response = ApiResponse {
success: true, success: true,
data: Some(parsed_result), data: Some(parsed_result),
error: None, error: None,
}; };
(StatusCode::OK, Json(response)) (StatusCode::OK, Json(response))
} }
Err(_) => { Err(_) => {
let response = ApiResponse { let response = ApiResponse {
success: true, success: true,
data: Some(serde_json::Value::String(result)), data: Some(serde_json::Value::String(result)),
error: None, error: None,
}; };
(StatusCode::OK, Json(response)) (StatusCode::OK, Json(response))
}
} }
} }
} Err(e) => {
Err(e) => { let response = ApiResponse {
let response = ApiResponse { success: false,
success: false, data: None,
data: None, error: Some(e.to_string()),
error: Some(e.to_string()), };
}; (StatusCode::BAD_REQUEST, Json(response))
(StatusCode::BAD_REQUEST, Json(response)) }
} }
} }
}

View File

@@ -1,3 +1,7 @@
/// Asynchronous service wrapper for `ItemService`.
///
/// Uses `tokio::task::spawn_blocking` to offload synchronous operations (DB/FS)
/// to a blocking thread pool, allowing non-blocking async usage in servers.
use crate::common::PIPESIZE; use crate::common::PIPESIZE;
use crate::config::Settings; use crate::config::Settings;
use crate::services::error::CoreError; use crate::services::error::CoreError;
@@ -15,6 +19,7 @@ use tokio::sync::Mutex;
/// It uses `tokio::task::spawn_blocking` to run synchronous database and filesystem operations /// It uses `tokio::task::spawn_blocking` to run synchronous database and filesystem operations
/// on a dedicated thread pool, preventing them from blocking the async runtime. /// on a dedicated thread pool, preventing them from blocking the async runtime.
#[allow(dead_code)] #[allow(dead_code)]
/// Async wrapper for ItemService operations.
pub struct AsyncItemService { pub struct AsyncItemService {
pub data_dir: PathBuf, pub data_dir: PathBuf,
db: Arc<Mutex<Connection>>, db: Arc<Mutex<Connection>>,
@@ -25,6 +30,19 @@ pub struct AsyncItemService {
#[allow(dead_code)] #[allow(dead_code)]
impl AsyncItemService { impl AsyncItemService {
/// Creates a new `AsyncItemService`.
///
/// # Arguments
///
/// * `data_dir` - Path to data directory.
/// * `db` - Arc-wrapped mutex for DB connection.
/// * `item_service` - Arc-wrapped ItemService.
/// * `cmd` - Arc-wrapped mutex for Clap command.
/// * `settings` - Arc-wrapped settings.
///
/// # Returns
///
/// A new `AsyncItemService`.
pub fn new( pub fn new(
data_dir: PathBuf, data_dir: PathBuf,
db: Arc<Mutex<Connection>>, db: Arc<Mutex<Connection>>,
@@ -41,6 +59,22 @@ impl AsyncItemService {
} }
} }
/// Internal helper to execute synchronous operations in a blocking task.
///
/// Spawns a blocking task with the DB connection and ItemService.
///
/// # Type Parameters
///
/// * `F` - Closure type.
/// * `T` - Return type.
///
/// # Arguments
///
/// * `f` - The synchronous closure to execute.
///
/// # Returns
///
/// Result of the closure, or CoreError on task failure.
async fn execute_blocking<F, T>(&self, f: F) -> Result<T, CoreError> async fn execute_blocking<F, T>(&self, f: F) -> Result<T, CoreError>
where where
F: FnOnce(&Connection, &ItemService) -> Result<T, CoreError> + Send + 'static, F: FnOnce(&Connection, &ItemService) -> Result<T, CoreError> + Send + 'static,

View File

@@ -142,6 +142,21 @@ impl MetaService {
} }
} }
/// Internal helper to process a meta plugin response and store metadata.
///
/// Iterates over the metadata entries in the response and stores each in the database
/// using `store_meta`. Logs warnings if storage fails.
///
/// # Arguments
///
/// * `conn` - Database connection.
/// * `item_id` - Item ID to associate with the metadata.
/// * `_plugin` - Reference to the plugin (unused).
/// * `response` - The plugin response containing metadata.
///
/// # Errors
///
/// Logs warnings for individual storage failures but does not return errors.
fn process_plugin_response( fn process_plugin_response(
&self, &self,
conn: &Connection, conn: &Connection,
@@ -163,6 +178,21 @@ impl MetaService {
} }
} }
/// Collects initial metadata from environment variables and hostname.
///
/// Gathers metadata from `KEEP_META_*` environment variables and adds hostname
/// if not already present.
///
/// # Returns
///
/// A `HashMap` of initial metadata key-value pairs.
///
/// # Examples
///
/// ```
/// let service = MetaService::new();
/// let initial_meta = service.collect_initial_meta();
/// ```
pub fn collect_initial_meta(&self) -> HashMap<String, String> { pub fn collect_initial_meta(&self) -> HashMap<String, String> {
let mut item_meta: HashMap<String, String> = crate::modes::common::get_meta_from_env(); let mut item_meta: HashMap<String, String> = crate::modes::common::get_meta_from_env();
@@ -176,6 +206,11 @@ impl MetaService {
} }
impl Default for MetaService { impl Default for MetaService {
/// Provides a default `MetaService` instance.
///
/// # Returns
///
/// A new `MetaService` via `new()`.
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }