diff --git a/crates/pinakes-plugin-api/src/manifest.rs b/crates/pinakes-plugin-api/src/manifest.rs index 0e52a62..0b6d9f4 100644 --- a/crates/pinakes-plugin-api/src/manifest.rs +++ b/crates/pinakes-plugin-api/src/manifest.rs @@ -34,7 +34,7 @@ pub struct PluginInfo { pub homepage: Option, pub license: Option, - /// Plugin kind(s) - e.g., ["media_type", "metadata_extractor"] + /// Plugin kind(s) - e.g., `media_type`, `metadata_extractor` pub kind: Vec, /// Binary configuration @@ -95,6 +95,12 @@ pub enum ManifestError { impl PluginManifest { /// Load and parse a plugin manifest from a TOML file + /// + /// # Errors + /// + /// Returns [`ManifestError::IoError`] if the file cannot be read, + /// [`ManifestError::ParseError`] if the TOML is invalid, or + /// [`ManifestError::ValidationError`] if the manifest fails validation. pub fn from_file(path: &Path) -> Result { let content = std::fs::read_to_string(path)?; let manifest: Self = toml::from_str(&content)?; @@ -103,6 +109,11 @@ impl PluginManifest { } /// Parse a manifest from TOML string + /// + /// # Errors + /// + /// Returns [`ManifestError::ParseError`] if the TOML is invalid, or + /// [`ManifestError::ValidationError`] if the manifest fails validation. pub fn parse_str(content: &str) -> Result { let manifest: Self = toml::from_str(content)?; manifest.validate()?; @@ -110,6 +121,11 @@ impl PluginManifest { } /// Validate the manifest + /// + /// # Errors + /// + /// Returns [`ManifestError::ValidationError`] if any required field is empty + /// or otherwise invalid. pub fn validate(&self) -> Result<(), ManifestError> { // Check API version format if self.plugin.api_version.is_empty() { @@ -163,6 +179,7 @@ impl PluginManifest { } /// Convert manifest capabilities to API capabilities + #[must_use] pub fn to_capabilities(&self) -> Capabilities { Capabilities { filesystem: FilesystemCapability { @@ -171,14 +188,14 @@ impl PluginManifest { .filesystem .read .iter() - .map(|s| s.into()) + .map(std::convert::Into::into) .collect(), write: self .capabilities .filesystem .write .iter() - .map(|s| s.into()) + .map(std::convert::Into::into) .collect(), }, network: NetworkCapability { @@ -201,6 +218,7 @@ impl PluginManifest { } /// Get plugin ID (derived from name and version) + #[must_use] pub fn plugin_id(&self) -> String { format!("{}@{}", self.plugin.name, self.plugin.version) } diff --git a/crates/pinakes-plugin-api/src/types.rs b/crates/pinakes-plugin-api/src/types.rs index 8975188..e39ea17 100644 --- a/crates/pinakes-plugin-api/src/types.rs +++ b/crates/pinakes-plugin-api/src/types.rs @@ -13,6 +13,7 @@ impl PluginId { Self(id.into()) } + #[must_use] pub fn as_str(&self) -> &str { &self.0 } @@ -91,7 +92,8 @@ pub struct Version { } impl Version { - pub fn new(major: u32, minor: u32, patch: u32) -> Self { + #[must_use] + pub const fn new(major: u32, minor: u32, patch: u32) -> Self { Self { major, minor, @@ -100,6 +102,7 @@ impl Version { } /// Parse version from string (e.g., "1.2.3") + #[must_use] pub fn parse(s: &str) -> Option { let parts: Vec<&str> = s.split('.').collect(); if parts.len() != 3 { @@ -115,7 +118,8 @@ impl Version { /// Check if this version is compatible with another version /// Compatible if major version matches and minor version is >= required - pub fn is_compatible_with(&self, required: &Version) -> bool { + #[must_use] + pub const fn is_compatible_with(&self, required: &Self) -> bool { self.major == required.major && self.minor >= required.minor } } diff --git a/crates/pinakes-plugin-api/src/wasm.rs b/crates/pinakes-plugin-api/src/wasm.rs index a1df9ea..7df1018 100644 --- a/crates/pinakes-plugin-api/src/wasm.rs +++ b/crates/pinakes-plugin-api/src/wasm.rs @@ -47,8 +47,8 @@ pub enum WasmResult { impl From> for WasmResult { fn from(r: Result) -> Self { match r { - Ok(v) => WasmResult::Ok(v), - Err(e) => WasmResult::Err(e), + Ok(v) => Self::Ok(v), + Err(e) => Self::Err(e), } } } @@ -112,22 +112,35 @@ pub struct HttpResponse { /// Helper functions for serializing/deserializing data across WASM boundary pub mod helpers { - use super::*; + use super::{Deserialize, PluginResponse, Serialize, WasmResult}; /// Serialize a value to bytes for passing to WASM + /// + /// # Errors + /// + /// Returns an error string if the value cannot be serialized to JSON. pub fn serialize(value: &T) -> Result, String> { - serde_json::to_vec(value).map_err(|e| format!("Serialization error: {}", e)) + serde_json::to_vec(value).map_err(|e| format!("Serialization error: {e}")) } /// Deserialize bytes from WASM to a value + /// + /// # Errors + /// + /// Returns an error string if the bytes cannot be deserialized as `T`. pub fn deserialize Deserialize<'de>>( bytes: &[u8], ) -> Result { serde_json::from_slice(bytes) - .map_err(|e| format!("Deserialization error: {}", e)) + .map_err(|e| format!("Deserialization error: {e}")) } /// Create a success response + /// + /// # Errors + /// + /// Returns an error string if `value` or the response envelope cannot be + /// serialized. pub fn ok_response( request_id: String, value: &T, @@ -138,6 +151,10 @@ pub mod helpers { } /// Create an error response + /// + /// # Errors + /// + /// Returns an error string if the response envelope cannot be serialized. pub fn error_response( request_id: String, error: String, diff --git a/crates/pinakes-plugin-api/tests/api.rs b/crates/pinakes-plugin-api/tests/api.rs index 36dc16e..a082f5c 100644 --- a/crates/pinakes-plugin-api/tests/api.rs +++ b/crates/pinakes-plugin-api/tests/api.rs @@ -106,10 +106,7 @@ async fn test_plugin_context_creation() { assert_eq!(context.data_dir, PathBuf::from("/data/test-plugin")); assert_eq!(context.cache_dir, PathBuf::from("/cache/test-plugin")); - assert_eq!( - context.config.get("enabled").unwrap(), - &serde_json::json!(true) - ); + assert_eq!(&context.config["enabled"], &serde_json::json!(true)); assert!(context.capabilities.network.enabled); assert_eq!( context.capabilities.max_memory_bytes, @@ -178,7 +175,7 @@ async fn test_extracted_metadata_structure() { assert_eq!(metadata.width, Some(1920)); assert_eq!(metadata.height, Some(1080)); assert_eq!(metadata.tags.len(), 2); - assert_eq!(metadata.custom_fields.get("color_space").unwrap(), "sRGB"); + assert_eq!(&metadata.custom_fields["color_space"], "sRGB"); } #[tokio::test] @@ -199,7 +196,7 @@ async fn test_search_query_serialization() { assert_eq!(deserialized.query_text, "nature landscape"); assert_eq!(deserialized.limit, 50); assert_eq!(deserialized.offset, 0); - assert_eq!(deserialized.filters.get("type").unwrap(), "image"); + assert_eq!(&deserialized.filters["type"], "image"); } #[tokio::test] @@ -439,7 +436,7 @@ async fn test_plugin_error_variants() { for error in errors { let serialized = serde_json::to_string(&error).unwrap(); let deserialized: PluginError = serde_json::from_str(&serialized).unwrap(); - assert_eq!(format!("{}", error), format!("{}", deserialized)); + assert_eq!(format!("{error}"), format!("{}", deserialized)); } }