From 7d698feacf51cd7ef61b99b0451bf29a884f6109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20V=C3=A9ril?= Date: Sat, 4 Jan 2025 13:52:25 +0100 Subject: [PATCH 1/5] Start working on documentation --- wslplugins-rs/Cargo.toml | 4 + wslplugins-rs/src/api/api_v1.rs | 107 ++++++++++++++++- wslplugins-rs/src/api/errors.rs | 32 +++++ .../src/api/errors/require_update_error.rs | 34 ++++++ wslplugins-rs/src/api/mod.rs | 27 ++++- wslplugins-rs/src/api/utils.rs | 8 +- .../src/core_distribution_information.rs | 36 +++++- wslplugins-rs/src/distribution_information.rs | 74 +++++++++++- wslplugins-rs/src/lib.rs | 52 ++++++++- .../src/offline_distribution_information.rs | 34 ++++++ wslplugins-rs/src/plugin/error.rs | 70 +++++++++++ wslplugins-rs/src/plugin/mod.rs | 44 ++++++- wslplugins-rs/src/plugin/utils.rs | 30 +++++ wslplugins-rs/src/plugin/wsl_plugin_v1.rs | 110 ++++++++++++++++-- wslplugins-rs/src/utils.rs | 10 ++ wslplugins-rs/src/wsl_context.rs | 43 +++++++ wslplugins-rs/src/wsl_session_information.rs | 74 +++++++++--- wslplugins-rs/src/wsl_vm_creation_settings.rs | 40 ++++++- 18 files changed, 783 insertions(+), 46 deletions(-) diff --git a/wslplugins-rs/Cargo.toml b/wslplugins-rs/Cargo.toml index 1fdfcd5..8609aef 100644 --- a/wslplugins-rs/Cargo.toml +++ b/wslplugins-rs/Cargo.toml @@ -26,3 +26,7 @@ optional = true sys = [] log-full = ["log", "log-instrument"] macro = ["dep:wslplugins-macro", "sys"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/wslplugins-rs/src/api/api_v1.rs b/wslplugins-rs/src/api/api_v1.rs index 799a0b2..68a1e53 100644 --- a/wslplugins-rs/src/api/api_v1.rs +++ b/wslplugins-rs/src/api/api_v1.rs @@ -1,4 +1,6 @@ extern crate wslplugins_sys; +#[cfg(doc)] +use super::Error; use super::Result; use crate::api::errors::require_update_error::Result as UpReqResult; use crate::utils::{cstring_from_str, encode_wide_null_terminated}; @@ -20,23 +22,57 @@ use windows::{ core::{Result as WinResult, GUID, PCSTR, PCWSTR}, Win32::Foundation::BOOL, }; -use wslplugins_sys::WSLVersion; + +use wslplugins_sys::{WSLPluginAPIV1, WSLVersion}; use super::utils::check_required_version_result; -pub struct ApiV1<'a>(&'a wslplugins_sys::WSLPluginAPIV1); -impl<'a> From<&'a wslplugins_sys::WSLPluginAPIV1> for ApiV1<'a> { - fn from(value: &'a wslplugins_sys::WSLPluginAPIV1) -> Self { +/// Represents a structured interface for interacting with the WSLPluginAPIV1 API. +/// This struct encapsulates the methods provided by the WSLPluginAPIV1 API, allowing +/// idiomatic interaction with the Windows Subsystem for Linux (WSL). +pub struct ApiV1<'a>(&'a WSLPluginAPIV1); + +/// Converts a raw reference to `WSLPluginAPIV1` into [ApiV1]. +impl<'a> From<&'a WSLPluginAPIV1> for ApiV1<'a> { + fn from(value: &'a WSLPluginAPIV1) -> Self { Self(value) } } impl ApiV1<'_> { + /// Returns the current version of the WSL API being used. + /// + /// This is useful for checking compatibility with specific API features. + /// + /// # Example + /// ```ignore + /// let api_v1: ApiV1 = ...; + /// let version = api_v1.version(); + /// println!( + /// "WSL API version: {}.{}.{}", + /// version.Major, version.Minor, version.Revision + /// ); #[cfg_attr(feature = "log-instrument", instrument)] pub fn version(&self) -> &WSLVersion { &self.0.Version } /// Create plan9 mount between Windows & Linux + + /// + /// Allows sharing a folder between the Windows host and the Linux environment. + /// + /// # Arguments + /// - `session`: The current WSL session. + /// - `windows_path`: The Windows path of the folder to be mounted. + /// - `linux_path`: The Linux path where the folder will be mounted. + /// - `read_only`: Whether the mount should be read-only. + /// - `name`: A custom name for the mount. + /// + /// # Example + /// ``` rust,ignore + /// api.mount_folder(&session, "C:\\path", "/mnt/path", false, "MyMount")?; + /// ``` + #[doc(alias = "MountFolder")] #[cfg_attr(feature = "log-instrument", instrument)] pub fn mount_folder, UP: AsRef>( &self, @@ -66,7 +102,36 @@ impl ApiV1<'_> { } /// Execute a program in the root namespace. + /// + /// This method runs a program in the root namespace of the current WSL session. It connects the standard input and output + /// streams of the executed process to a [`TcpStream`], allowing interaction with the process. + /// + /// # Arguments + /// - `session`: The current WSL session. + /// - `path`: Path to the program to execute. + /// - `args`: Arguments to pass to the program (including `arg0`). + /// + /// # Returns + /// On success, this method returns a [`TcpStream`] connected to the standard input and output streams of the executed process. + /// - **Standard Input**: Data written to the stream will be sent to the process. + /// - **Standard Output**: Data output by the process will be readable from the stream. + /// + /// # Errors + /// This method can return the following a [`windows::core::Error`]: If the underlying Windows API call fails. + /// + /// # Example + /// ```rust,ignore + /// let stream = api.execute_binary(&session, "/bin/ls", ["/bin/ls", "-l", "/etc"])?; + /// // Write to the process (stdin) + /// writeln!(stream, "input data").unwrap(); + /// + /// // Read from the process (stdout) + /// let mut buffer = String::new(); + /// stream.read_to_string(&mut buffer).unwrap(); + /// println!("Process output: {}", buffer); + /// ``` #[cfg_attr(feature = "log-instrument", instrument)] + #[doc(alias = "ExecuteBinary")] pub fn execute_binary>( &self, session: &WSLSessionInformation, @@ -109,8 +174,39 @@ impl ApiV1<'_> { let error_vec = encode_wide_null_terminated(error); unsafe { self.0.PluginError.unwrap_unchecked()(PCWSTR::from_raw(error_vec.as_ptr())).ok() } } + /// Execute a program in a user distribution - /// Introduced in 2.1.2 + /// + /// # Introduced + /// This requires API version 2.1.2 or later. + /// + /// # Arguments + /// - `session`: The current WSL session. + /// - `distribution_id`: The ID of the target distribution. + /// - `path`: Path to the program to execute. + /// - `args`: Arguments to pass to the program (including arg0). + /// + /// # Returns + /// A [`TcpStream`] connected to the process's stdin and stdout. + /// + /// # Errors + /// This function may return the following errors: + /// + /// - [`Error::RequiresUpdate`]: If the API version is lower than 2.1.2. + /// - [`Error::WinError`]: If the Windows API fails during execution. + /// + /// # Example + /// ```rust,ignore + /// let stream = api.execute_binary_in_distribution(&session, "/bin/ls", ["/bin/ls", "-l", "/etc"])?; + /// // Write to the process (stdin) + /// writeln!(stream, "input data").unwrap(); + /// + /// // Read from the process (stdout) + /// let mut buffer = String::new(); + /// stream.read_to_string(&mut buffer).unwrap(); + /// println!("Process output: {}", buffer); + /// ``` + #[doc(alias = "ExecuteBinaryInDistribution")] #[cfg_attr(feature = "log-instrument", instrument)] pub fn execute_binary_in_distribution>( &self, @@ -151,6 +247,7 @@ impl ApiV1<'_> { }; Ok(stream) } + fn check_required_version(&self, version: &WSLVersion) -> UpReqResult<()> { check_required_version_result(self.version(), version) } diff --git a/wslplugins-rs/src/api/errors.rs b/wslplugins-rs/src/api/errors.rs index 73c33d7..a729900 100644 --- a/wslplugins-rs/src/api/errors.rs +++ b/wslplugins-rs/src/api/errors.rs @@ -1,18 +1,39 @@ +//! # Error Handling for WSL Plugins +//! +//! This module defines a unified error type for handling errors in WSL plugins, combining +//! both custom plugin-specific errors and Windows system errors. It provides seamless +//! interoperability with Windows APIs using `HRESULT`. + use thiserror::Error; pub mod require_update_error; pub use require_update_error::Error as RequireUpdateError; use windows::core::{Error as WinError, HRESULT}; use wslplugins_sys::WSL_E_PLUGIN_REQUIRES_UPDATE; +/// A comprehensive error type for WSL plugins. +/// +/// This enum encapsulates two main error categories: +/// - Plugin-specific errors (`RequireUpdateError`). +/// - Errors originating from Windows APIs (`WinError`). #[derive(Debug, Error)] pub enum Error { + /// Indicates that the current WSL version does not meet the required version. #[error("Require Update Error")] RequiresUpdate(#[from] RequireUpdateError), + + /// Represents an error from Windows APIs. #[error("Windows Error: {0}")] WinError(#[from] WinError), } impl From for HRESULT { + /// Converts the `Error` enum into an `HRESULT` code. + /// + /// - Maps `Error::RequiresUpdate` to `WSL_E_PLUGIN_REQUIRES_UPDATE`. + /// - Maps `Error::WinError` to its corresponding `HRESULT` code. + /// + /// # Returns + /// An `HRESULT` code representing the error. fn from(value: Error) -> Self { match value { Error::RequiresUpdate(err) => err.into(), @@ -22,6 +43,13 @@ impl From for HRESULT { } impl From for WinError { + /// Converts the `Error` enum into a `WinError`. + /// + /// - Maps `Error::RequiresUpdate` to `WSL_E_PLUGIN_REQUIRES_UPDATE`. + /// - Maps `Error::WinError` directly to itself. + /// + /// # Returns + /// A `WinError` representing the error. fn from(value: Error) -> Self { match value { Error::RequiresUpdate { .. } => WSL_E_PLUGIN_REQUIRES_UPDATE.into(), @@ -30,4 +58,8 @@ impl From for WinError { } } +/// A type alias for results using the custom `Error` type. +/// +/// This alias simplifies the return type for functions that may fail, ensuring consistency +/// across the codebase. pub type Result = std::result::Result; diff --git a/wslplugins-rs/src/api/errors/require_update_error.rs b/wslplugins-rs/src/api/errors/require_update_error.rs index f4d1d06..8cb8fdf 100644 --- a/wslplugins-rs/src/api/errors/require_update_error.rs +++ b/wslplugins-rs/src/api/errors/require_update_error.rs @@ -1,15 +1,39 @@ +//! # WSL Plugin Error Handling +//! +//! This module provides an error type and result alias to handle scenarios where the current WSL version +//! does not meet the required version. It integrates with Windows error codes for seamless interop +//! with WSL APIs. + use thiserror::Error; use windows::core::HRESULT; use wslplugins_sys::{WSLVersion, WSL_E_PLUGIN_REQUIRES_UPDATE}; +/// Represents an error when the current WSL version is unsupported. +/// +/// This error is returned when the WSL version being used does not satisfy +/// the required version for a plugin. +/// +/// # Fields +/// - `current_version`: The current WSL version. +/// - `required_version`: The required WSL version. #[derive(Debug, Error)] #[error("WSLVersion unsupported: current version {current_version}, required version {required_version}")] pub struct Error { + /// The current version of WSL. pub current_version: WSLVersion, + /// The required version of WSL. pub required_version: WSLVersion, } impl Error { + /// Creates a new `Error` with the specified current and required WSL versions. + /// + /// # Arguments + /// - `current_version`: The version currently in use. + /// - `required_version`: The version required for compatibility. + /// + /// # Returns + /// A new instance of `Error`. pub fn new(current_version: WSLVersion, required_version: WSLVersion) -> Self { Self { current_version, @@ -19,9 +43,19 @@ impl Error { } impl From for HRESULT { + /// Converts the `Error` into an `HRESULT` error code. + /// + /// This implementation maps the custom `Error` to the `WSL_E_PLUGIN_REQUIRES_UPDATE` HRESULT. + /// + /// # Returns + /// - `[WSL_E_PLUGIN_REQUIRES_UPDATE]: Indicates the WSL version is insufficient for the plugin. fn from(_: Error) -> Self { WSL_E_PLUGIN_REQUIRES_UPDATE } } +/// A result type alias for operations that may return a WSL plugin error. +/// +/// This alias provides convenience when working with operations that might fail due to unsupported +/// WSL versions. pub type Result = std::result::Result; diff --git a/wslplugins-rs/src/api/mod.rs b/wslplugins-rs/src/api/mod.rs index 9dbc121..01cfcc7 100644 --- a/wslplugins-rs/src/api/mod.rs +++ b/wslplugins-rs/src/api/mod.rs @@ -1,5 +1,28 @@ -pub mod api_v1; +//! # API Module +//! +//! This module provides the core functionality for interacting with the WSL plugin API. +//! It includes abstractions for API interactions, error handling, and utility functions. + +mod api_v1; pub mod errors; + +/// The `ApiV1` struct provides an interface to interact with version 1 of the WSL Plugin API. +/// +/// It encapsulates low-level interactions with the API and exposes a safe and idiomatic Rust interface. pub use api_v1::ApiV1; -pub use errors::{Error, Result}; + +/// The `Error` type represents errors that may occur while using the WSL Plugin API. +/// +/// This type encapsulates various error scenarios, providing a consistent and ergonomic way +/// to handle failures. +pub use errors::Error; + +/// A specialized `Result` type for operations that may fail in the context of the WSL Plugin API. +/// +/// This alias simplifies the function signatures by standardizing error handling. +pub use errors::Result; + +/// The `utils` module provides utility functions and helpers for working with the WSL Plugin API. +/// +/// These utilities simplify common tasks, such as version checking or string manipulation. pub mod utils; diff --git a/wslplugins-rs/src/api/utils.rs b/wslplugins-rs/src/api/utils.rs index 25effbc..b22e581 100644 --- a/wslplugins-rs/src/api/utils.rs +++ b/wslplugins-rs/src/api/utils.rs @@ -1,6 +1,10 @@ -use crate::WSLContext; +//! # WSL Version Checking Utilities +//! +//! This module provides utilities to verify that the current WSL version meets the required version for plugin compatibility. +//! It includes functions to perform version checks and unit tests to ensure correctness. use super::errors::require_update_error::{Error, Result}; +use crate::WSLContext; use wslplugins_sys::WSLVersion; pub(crate) fn check_required_version_result( @@ -30,6 +34,7 @@ mod tests { use super::*; use wslplugins_sys::WSLVersion; + /// Tests that `check_required_version_result` returns `Ok` when the current version meets the requirement. #[test] fn test_check_required_version_result_ok() { let current_version = WSLVersion::new(2, 1, 3); @@ -40,6 +45,7 @@ mod tests { assert!(result.is_ok()); } + /// Tests that `check_required_version_result` returns `Err` when the current version is insufficient. #[test] fn test_check_required_version_result_error() { let current_version = WSLVersion::new(2, 1, 1); diff --git a/wslplugins-rs/src/core_distribution_information.rs b/wslplugins-rs/src/core_distribution_information.rs index 1ea95c0..73860e8 100644 --- a/wslplugins-rs/src/core_distribution_information.rs +++ b/wslplugins-rs/src/core_distribution_information.rs @@ -1,10 +1,42 @@ +//! # Core Distribution Information +//! +//! This module defines a trait to represent core information about a WSL distribution. +//! It provides methods to retrieve essential details such as the distribution ID, name, +//! and package family name, offering a consistent interface for interacting with WSL distributions. +//! +//! ## Overview +//! The `CoreDistributionInformation` trait is designed to abstract the key properties +//! of a distribution. Implementing this trait allows for seamless integration with systems +//! that need to handle multiple distributions in a consistent manner. + use std::ffi::OsString; use windows::core::GUID; +/// A trait representing the core information of a WSL distribution. +/// +/// This trait abstracts the common properties of a WSL distribution, such as its unique ID, +/// display name, and package family name (if applicable). pub trait CoreDistributionInformation { - /// Distribution ID, guaranteed to be the same accross reboots + /// Retrieves the unique ID of the distribution. + /// + /// The ID is guaranteed to remain the same across reboots. + /// + /// # Returns + /// A reference to the [GUID] representing the distribution's unique identifier. fn id(&self) -> &GUID; + + /// Retrieves the name of the distribution. + /// + /// # Returns + /// An [OsString] containing the display name of the distribution. fn name(&self) -> OsString; - /// Package family name, if the distribution is packaged + + /// Retrieves the package family name of the distribution, if available. + /// + /// The package family name is applicable if the distribution is packaged. + /// + /// # Returns + /// - `Some`(OsString)`: If the distribution has a package family name. + /// - `None`: If the distribution is not packaged or the information is unavailable. fn package_family_name(&self) -> Option; } diff --git a/wslplugins-rs/src/distribution_information.rs b/wslplugins-rs/src/distribution_information.rs index a8ae596..2d2b474 100644 --- a/wslplugins-rs/src/distribution_information.rs +++ b/wslplugins-rs/src/distribution_information.rs @@ -1,24 +1,59 @@ +//! # Distribution Information +//! +//! This module provides a safe abstraction for accessing information about a WSL distribution. +//! It wraps the `WSLDistributionInformation` structure from the WSL Plugin API and implements +//! the `CoreDistributionInformation` trait for consistent access to distribution details. +//! +//! ## Overview +//! The `DistributionInformation` struct provides methods to retrieve: +//! - Distribution ID +//! - Distribution name +//! - Package family name (if applicable) +//! - Process ID (PID) of the init process (requires API version 2.0.5 or higher) +//! - PID namespace + extern crate wslplugins_sys; use crate::api::{ - errors::require_update_error::Result, utils::check_required_version_result_from_context, + errors::require_update_error::{Error, Result}, + utils::check_required_version_result_from_context, }; use crate::core_distribution_information::CoreDistributionInformation; use crate::WSLContext; -use std::fmt::Debug; +use std::ffi::OsString; +use std::fmt::{Debug, Display}; use std::hash::Hash; -use std::{ffi::OsString, fmt::Display, os::windows::ffi::OsStringExt}; +use std::os::windows::ffi::OsStringExt; use windows::core::GUID; use wslplugins_sys::WSLVersion; + +/// Represents detailed information about a WSL distribution. +/// +/// This struct wraps the `WSLDistributionInformation` from the WSL Plugin API and provides +/// safe, idiomatic Rust access to its fields. pub struct DistributionInformation<'a>(&'a wslplugins_sys::WSLDistributionInformation); impl<'a> From<&'a wslplugins_sys::WSLDistributionInformation> for DistributionInformation<'a> { + /// Creates a `DistributionInformation` instance from a reference to the raw WSL Plugin API structure. + /// + /// # Arguments + /// - `ptr`: A reference to a `WSLDistributionInformation` instance. + /// + /// # Returns + /// A wrapped `DistributionInformation` instance. fn from(ptr: &'a wslplugins_sys::WSLDistributionInformation) -> Self { Self(ptr) } } impl DistributionInformation<'_> { - /// Pid of the init process. Introduced in 2.0.5 + /// Retrieves the PID of the init process. + /// + /// This requires API version 2.0.5 or higher. If the current API version does not meet + /// the requirement, an error is returned. + /// + /// # Returns + /// - `Ok(u32)`: The PID of the init process. + /// - `Err(Error)`: If the API version is insufficient. pub fn init_pid(&self) -> Result { check_required_version_result_from_context( WSLContext::get_current_or_panic(), @@ -27,20 +62,38 @@ impl DistributionInformation<'_> { Ok(self.0.InitPid) } + /// Retrieves the PID namespace for the distribution. + /// + /// # Returns + /// The PID namespace as a `u64`. + /// pub fn pid_namespace(&self) -> u64 { self.0.PidNamespace } } impl CoreDistributionInformation for DistributionInformation<'_> { + /// Retrieves the unique ID of the distribution. + /// + /// # Returns + /// A reference to the [GUID] representing the distribution's unique identifier. fn id(&self) -> &GUID { &self.0.Id } + /// Retrieves the name of the distribution. + /// + /// # Returns + /// An [OsString] containing the display name of the distribution. fn name(&self) -> OsString { unsafe { OsString::from_wide(self.0.Name.as_wide()) } } + /// Retrieves the package family name of the distribution, if available. + /// + /// # Returns + /// - `Some(OsString)`: If the distribution has a package family name. + /// - `None`: If the distribution is not packaged or the information is unavailable. fn package_family_name(&self) -> Option { unsafe { let ptr = self.0.PackageFamilyName; @@ -57,24 +110,37 @@ impl PartialEq for DistributionInformation<'_> where T: CoreDistributionInformation, { + /// Compares two distributions for equality based on their IDs. fn eq(&self, other: &T) -> bool { self.id() == other.id() } } impl Hash for DistributionInformation<'_> { + /// Computes a hash based on the distribution's ID. fn hash(&self, state: &mut H) { self.id().hash(state); } } impl Display for DistributionInformation<'_> { + /// Formats the distribution information for display. + /// + /// The output includes the distribution's name and ID. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unsafe { write!(f, "{:} {{{:?}}}", self.0.Name.display(), self.0.Id) } } } impl Debug for DistributionInformation<'_> { + /// Formats the distribution information for debugging. + /// + /// The output includes: + /// - Name + /// - ID + /// - Package family name (if available) + /// - PID namespace + /// - Init PID (if available and the API version supports it) fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut dbg = f.debug_struct("DistributionInformation"); dbg.field("name", &self.name()) diff --git a/wslplugins-rs/src/lib.rs b/wslplugins-rs/src/lib.rs index 6d3fd26..327902b 100644 --- a/wslplugins-rs/src/lib.rs +++ b/wslplugins-rs/src/lib.rs @@ -1,21 +1,69 @@ -pub extern crate wslplugins_sys; +//! # WSLPlugin-rs +//! +//! This is the main entry point for the **WSLPlugin-rs** crate, a framework designed for creating +//! plugins for the **Windows Subsystem for Linux (WSL)** using idiomatic Rust. +//! +//! ## Overview +//! +//! WSLPlugin-rs simplifies the development of [WSL plugins](https://learn.microsoft.com/windows/wsl/wsl-plugins?utm_source=chatgpt.com) by providing higher-level abstractions +//! built on top of the raw APIs. This crate exports useful modules and types for plugin creation, +//! such as session management, VM handling, and distribution operations. +//! +//! ## Usage +//! +//! Use the exposed modules and types to build custom WSL plugins. Conditional features like `macro` +//! enable procedural macros for simplifying the plugin development process. +//! +//! ### Example +//! +//! ```rust +//! #[cfg(feature = "macro")] +//! use wslplugins_rs::{plugin::WSLPluginV1, WSLContext}; +//! use windows::core::Result as WinResult; +//! use wslplugins_rs::wsl_plugin_v1; +//! pub(crate) struct MyPlugin { +//! context: &'static WSLContext, +//! } +//! #[wsl_plugin_v1(2, 0, 5)] +//! impl WSLPluginV1 for MyPlugin { +//! fn try_new(context: &'static WSLContext) -> WinResult { +//! Ok(MyPlugin { context }) +//! } +//! } +//! ``` +// Enable doc_cfg if docrs +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +/// Provides interfaces for interacting with WSL plugin APIs. pub mod api; + +// Internal modules for managing specific WSL features. mod core_distribution_information; mod distribution_information; mod offline_distribution_information; -pub mod plugin; mod utils; mod wsl_context; mod wsl_session_information; pub mod wsl_user_configuration; pub use wsl_user_configuration::WSLUserConfiguration; mod wsl_vm_creation_settings; +#[cfg(doc)] +use crate::plugin::WSLPluginV1; +/// Tools and utilities for creating custom WSL plugins. +pub mod plugin; + +// Re-exports for core structures to simplify usage. pub use core_distribution_information::CoreDistributionInformation; pub use distribution_information::DistributionInformation; pub use wsl_context::WSLContext; pub use wsl_session_information::WSLSessionInformation; pub use wsl_vm_creation_settings::WSLVmCreationSettings; + +/// Re-exports procedural macros when the `macro` feature is enabled. +/// It allow to mark a plugin struct (that implement [WSLPluginV1] trait) to be easely integrated to the WSL plugin system without writing manually C code for entry point or hooks. #[cfg(feature = "macro")] pub use wslplugins_macro::wsl_plugin_v1; + +/// Re-exports the `wslplugins_sys` crate as `sys` when the `sys` feature is enabled. #[cfg(feature = "sys")] pub use wslplugins_sys as sys; diff --git a/wslplugins-rs/src/offline_distribution_information.rs b/wslplugins-rs/src/offline_distribution_information.rs index 46099cc..9008dd8 100644 --- a/wslplugins-rs/src/offline_distribution_information.rs +++ b/wslplugins-rs/src/offline_distribution_information.rs @@ -1,3 +1,8 @@ +//! # Offline Distribution Information +//! +//! This module provides an abstraction over `WslOfflineDistributionInformation` from the WSL Plugin API, +//! offering a safe and idiomatic Rust interface for accessing offline distribution details. + extern crate wslplugins_sys; use crate::core_distribution_information::CoreDistributionInformation; use std::{ @@ -8,25 +13,46 @@ use std::{ }; use windows::core::GUID; +/// A wrapper around `WslOfflineDistributionInformation` providing a safe interface. +/// +/// This struct allows access to the details of an offline WSL distribution, including +/// its ID, name, and optional package family name. +/// +/// # Lifetime Parameters +/// - `'a`: The lifetime of the referenced `WslOfflineDistributionInformation` instance. pub struct OfflineDistributionInformation<'a>( &'a wslplugins_sys::WslOfflineDistributionInformation, ); impl<'a> OfflineDistributionInformation<'a> { + /// Creates a new `OfflineDistributionInformation` instance from a raw pointer. + /// + /// # Arguments + /// - `ptr`: A reference to a `WslOfflineDistributionInformation` instance. + /// + /// # Returns + /// A safe wrapper around the provided pointer. pub fn from(ptr: &'a wslplugins_sys::WslOfflineDistributionInformation) -> Self { Self(ptr) } } impl CoreDistributionInformation for OfflineDistributionInformation<'_> { + /// Retrieves the [GUID] of the offline distribution. fn id(&self) -> &GUID { &self.0.Id } + /// Retrieves the name of the offline distribution as an [OsString]. fn name(&self) -> OsString { unsafe { OsString::from_wide(self.0.Name.as_wide()) } } + /// Retrieves the package family name of the offline distribution, if available. + /// + /// # Returns + /// - `Some(OsString)`: If the package family name is set. + /// - `None`: If the package family name is null or empty. fn package_family_name(&self) -> Option { unsafe { let ptr = self.0.PackageFamilyName; @@ -43,24 +69,32 @@ impl PartialEq for OfflineDistributionInformation<'_> where T: CoreDistributionInformation, { + /// Compares two distributions by their IDs for equality. fn eq(&self, other: &T) -> bool { self.id() == other.id() } } impl Hash for OfflineDistributionInformation<'_> { + /// Computes a hash based on the distribution's ID. fn hash(&self, state: &mut H) { self.id().hash(state); } } impl Display for OfflineDistributionInformation<'_> { + /// Formats the offline distribution information for display. + /// + /// The output includes the distribution's name and ID. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unsafe { write!(f, "{:} {{{:?}}}", self.0.Name.display(), self.0.Id) } } } impl Debug for OfflineDistributionInformation<'_> { + /// Formats the offline distribution information for debugging. + /// + /// The output includes the distribution's name, ID, and package family name. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DistributionInformation") .field("name", &self.name()) diff --git a/wslplugins-rs/src/plugin/error.rs b/wslplugins-rs/src/plugin/error.rs index d6dd090..5cb1110 100644 --- a/wslplugins-rs/src/plugin/error.rs +++ b/wslplugins-rs/src/plugin/error.rs @@ -1,3 +1,9 @@ +//! # WSL Plugin Error Handling +//! +//! This module defines a custom error type for handling errors in WSL plugins. +//! It integrates with Windows APIs, supports error codes and optional error messages, +//! and provides utility methods for error creation and consumption. + use crate::WSLContext; #[cfg(feature = "log")] use log::debug; @@ -6,15 +12,29 @@ use std::num::NonZeroI32; use thiserror::Error; use windows::core::{Error as WinError, HRESULT}; +/// A specialized result type for operations that may return a WSL plugin error. +/// +/// This alias simplifies the function signatures throughout the WSL plugin codebase. pub type Result = std::result::Result; +/// Represents an error in the WSL plugin system. +/// +/// This struct encapsulates: +/// - An error code (`HRESULT`) derived from Windows APIs. +/// - An optional error message (`OsString`). #[derive(Debug, Error)] pub struct Error { + /// The error code associated with the failure. code: NonZeroI32, + /// An optional error message providing more context about the error. message: Option, } impl std::fmt::Display for Error { + /// Formats the error for display. + /// + /// If an error message is present, it is included in the output. + /// Otherwise, only the error code is displayed. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.message { Some(message) => write!( @@ -29,6 +49,14 @@ impl std::fmt::Display for Error { } impl Error { + /// Creates a new error with a specified code and optional message. + /// + /// # Arguments + /// - `code`: The HRESULT representing the error code. + /// - `message`: An optional error message. + /// + /// # Returns + /// A new instance of `Error`. pub fn new(code: HRESULT, message: Option<&OsStr>) -> Self { let code = if code.is_ok() { WinError::from_hresult(code).code() @@ -43,22 +71,52 @@ impl Error { } } + /// Creates an error with only an error code. + /// + /// # Arguments + /// - `code`: The HRESULT representing the error code. + /// + /// # Returns + /// A new instance of `Error` without an associated message. pub fn with_code(code: HRESULT) -> Self { Self::new(code, None) } + /// Creates an error with both a code and a message. + /// + /// # Arguments + /// - `code`: The HRESULT representing the error code. + /// - `message`: An associated error message. + /// + /// # Returns + /// A new instance of `Error`. pub fn with_message(code: HRESULT, message: &OsStr) -> Self { Self::new(code, Some(message)) } + /// Retrieves the error code as an `HRESULT`. + /// + /// # Returns + /// The error code wrapped in an `HRESULT`. pub fn code(&self) -> HRESULT { HRESULT(self.code.get()) } + /// Retrieves the optional error message. + /// + /// # Returns + /// A reference to the error message, if present. pub fn message(&self) -> Option<&OsStr> { self.message.as_deref() } + /// Consumes the error and optionally sets the plugin's error message in the WSL context. + /// + /// # Type Parameters + /// - `R`: The type to return after consuming the error. + /// + /// # Returns + /// The consumed error converted to the specified type. pub(crate) fn consume_error_message_unwrap>(self) -> R { if let Some(ref mess) = self.message { if let Some(context) = WSLContext::get_current() { @@ -78,6 +136,10 @@ impl Error { } impl From for WinError { + /// Converts the `Error` into a `WinError`. + /// + /// # Returns + /// A `WinError` constructed from the error's code and message. fn from(value: Error) -> Self { match value.message { Some(ref message) => { @@ -90,6 +152,10 @@ impl From for WinError { } impl From for Error { + /// Converts a `WinError` into an `Error`. + /// + /// # Returns + /// An `Error` containing the code and message from the `WinError`. fn from(value: WinError) -> Self { let os_message = if !value.message().is_empty() { Some(OsString::from(value.message())) @@ -105,6 +171,10 @@ impl From for Error { } impl From for Error { + /// Converts an `HRESULT` into an `Error`. + /// + /// # Returns + /// An `Error` containing the `HRESULT` as its code. fn from(value: HRESULT) -> Self { Self::new(value, None) } diff --git a/wslplugins-rs/src/plugin/mod.rs b/wslplugins-rs/src/plugin/mod.rs index 53eda11..887be71 100644 --- a/wslplugins-rs/src/plugin/mod.rs +++ b/wslplugins-rs/src/plugin/mod.rs @@ -1,6 +1,42 @@ -mod error; -mod wsl_plugin_v1; -pub use error::{Error, Result}; -pub use wsl_plugin_v1::WSLPluginV1; +//! # WSL Plugin Framework +//! +//! This module serves as the main entry point for creating WSL plugins using Rust. +//! It provides error handling, utilities, and the WSL Plugin v1 interface for seamless integration +//! with the Windows Subsystem for Linux (WSL) plugin system. + +/// Error handling utilities for WSL plugins. +/// +/// This module defines custom error types and result aliases to simplify and standardize +/// error handling throughout the WSL plugin framework. +pub mod error; + +/// The WSL Plugin v1 interface. +/// +/// This module provides the implementation and traits required to define and interact with +/// WSL plugins compatible with the Plugin API version 1. +pub mod wsl_plugin_v1; + +/// Utility functions for WSL plugin development. +/// +/// This module includes helper functions and utilities to facilitate common tasks in WSL plugin creation. pub mod utils; + +/// The primary error type for the WSL plugin framework. +/// +/// Refer to [`error::Error`] for more details. +pub use error::Error; + +/// A specialized result type for operations within the WSL plugin framework. +/// +/// Refer to [`error::Result`] for more details. +pub use error::Result; + +/// The core interface for WSL plugins. +/// +/// Refer to [`wsl_plugin_v1::WSLPluginV1`] for more details. +pub use wsl_plugin_v1::WSLPluginV1; + +/// A utility function to create a plugin with a specified required version. +/// +/// Refer to [`utils::create_plugin_with_required_version`] for more details. pub use utils::create_plugin_with_required_version; diff --git a/wslplugins-rs/src/plugin/utils.rs b/wslplugins-rs/src/plugin/utils.rs index 2cdf55a..01d472c 100644 --- a/wslplugins-rs/src/plugin/utils.rs +++ b/wslplugins-rs/src/plugin/utils.rs @@ -1,3 +1,8 @@ +//! # Plugin Creation and Result Handling Utilities +//! +//! This module provides utility functions for creating WSL plugins and handling results, +//! enabling smooth integration with the WSL Plugin API. + use windows::{ core::{Error as WinError, Result as WinResult}, Win32::Foundation::ERROR_ALREADY_INITIALIZED, @@ -8,6 +13,31 @@ use crate::{api::ApiV1, WSLContext}; use super::{Result, WSLPluginV1}; +/// Creates a WSL plugin instance with a specified required API version. +/// +/// This function ensures that the provided WSL Plugin API meets the required version before +/// creating a new plugin instance. If the API version does not meet the requirements, an error is returned. +/// +/// # Type Parameters +/// - `T`: A type implementing the [WSLPluginV1] trait, representing the plugin to create. +/// +/// # Arguments +/// - `api`: A reference to the WSL Plugin API version 1 structure. +/// - `required_major`: The required major version of the API. +/// - `required_minor`: The required minor version of the API. +/// - `required_revision`: The required revision of the API. +/// +/// # Returns +/// - `Ok(T)`: The created plugin instance. +/// - `Err(WinError)`: If the API version is insufficient or the plugin is already initialized. +/// +/// # Errors +/// - Returns [WinError]`::from(`[ERROR_ALREADY_INITIALIZED]`)` if a plugin is already initialized. +/// - Returns [WinError]`::from(`[WSL_E_PLUGIN_REQUIRES_UPDATE](wslplugins_sys::WSL_E_PLUGIN_REQUIRES_UPDATE)`)` error if the API version is insufficient. +/// +/// # Safety +/// This function calls an unsafe API to check the reqred version. Ensure the provided API pointer +/// is valid and correctly initialized. pub fn create_plugin_with_required_version( api: &'static WSLPluginAPIV1, required_major: u32, diff --git a/wslplugins-rs/src/plugin/wsl_plugin_v1.rs b/wslplugins-rs/src/plugin/wsl_plugin_v1.rs index 1f89d71..0b49426 100644 --- a/wslplugins-rs/src/plugin/wsl_plugin_v1.rs +++ b/wslplugins-rs/src/plugin/wsl_plugin_v1.rs @@ -1,4 +1,10 @@ -pub use super::error::Result; +//! # WSL Plugin v1 Trait +//! +//! This module defines the `WSLPluginV1` trait, which provides a framework for handling +//! synchronous notifications sent to a WSL plugin. The trait defines lifecycle events +//! for managing the state of the WSL VM, distributions, and related settings. + +use super::error::Result; use crate::WSLContext; use crate::{ distribution_information::DistributionInformation, @@ -10,10 +16,56 @@ use std::marker::Sized; use windows::core::Result as WinResult; /// Trait defining synchronous notifications sent to the plugin. +/// +/// Implementors of this trait must provide methods for responding to key lifecycle events +/// in the WSL plugin system. Each method corresponds to a specific event, such as the start +/// or stop of a WSL VM or distribution. +/// +/// # Requirements +/// - The trait is `Sized` and `Sync`, ensuring safe concurrent usage and instantiation. +/// +/// # Example +/// ```rust +/// use wslplugins_rs::{plugin::{WSLPluginV1, Result}, WSLContext, WSLSessionInformation, WSLVmCreationSettings}; +/// use windows::core::Result as WinResult; +/// +/// struct MyPlugin; +/// +/// impl WSLPluginV1 for MyPlugin { +/// fn try_new(context: &'static WSLContext) -> WinResult { +/// Ok(MyPlugin) +/// } +/// +/// fn on_vm_started( +/// &self, +/// session: &WSLSessionInformation, +/// user_settings: &WSLVmCreationSettings, +/// ) -> Result<()> { +/// println!("VM started"); +/// Ok(()) +/// } +/// } +/// ``` pub trait WSLPluginV1: Sized + Sync { + /// Attempts to create a new instance of the plugin. + /// + /// # Arguments + /// - `context`: A reference to the `WSLContext` providing access to the plugin API. + /// + /// # Returns + /// - `Ok(Self)`: If the plugin was successfully initialized. + /// - `Err(WinError)`: If initialization fails. fn try_new(context: &'static WSLContext) -> WinResult; /// Called when the VM has started. + /// + /// # Arguments + /// - `session`: Information about the current session. + /// - `user_settings`: Custom user settings for the VM creation. + /// + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(`Error`)`: If the event handling failed. #[allow(unused_variables)] fn on_vm_started( &self, @@ -24,12 +76,27 @@ pub trait WSLPluginV1: Sized + Sync { } /// Called when the VM is about to stop. + /// + /// # Arguments + /// - `session`: Information about the current session. + /// + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(WinError)`: If the event handling failed. #[allow(unused_variables)] fn on_vm_stopping(&self, session: &WSLSessionInformation) -> WinResult<()> { Ok(()) } /// Called when a distribution has started. + /// + /// # Arguments + /// - `session`: Information about the current session. + /// - `distribution`: Information about the distribution. + /// + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(Error)`: If the event handling failed. #[allow(unused_variables)] fn on_distribution_started( &self, @@ -41,8 +108,16 @@ pub trait WSLPluginV1: Sized + Sync { /// Called when a distribution is about to stop. /// - /// Note: It's possible that stopping a distribution fails (for instance, if a file is in use). - /// In this case, this notification might be called multiple times for the same distribution. + /// # Arguments + /// - `session`: Information about the current session. + /// - `distribution`: Information about the distribution. + /// + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(WinError)`: If the event handling failed. + /// + /// # Notes + /// - This method might be called multiple times for the same distribution if stopping fails. #[allow(unused_variables)] fn on_distribution_stopping( &self, @@ -52,10 +127,18 @@ pub trait WSLPluginV1: Sized + Sync { Ok(()) } - /// Called when a distribution is registered or unregistered. + /// Called when a distribution is registered. + /// + /// # Arguments + /// - `session`: Information about the current session. + /// - `distribution`: Offline information about the distribution. /// - /// Returning failure will NOT cause the operation to fail. - /// Introduced in 2.1.2 + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(WinError)`: If the event handling failed. + /// + /// # Notes + /// - Introduced in API version 2.1.2. #[allow(unused_variables)] fn on_distribution_registered( &self, @@ -64,10 +147,19 @@ pub trait WSLPluginV1: Sized + Sync { ) -> WinResult<()> { Ok(()) } - /// Called when a distribution is registered or unregisteed. + + /// Called when a distribution is unregistered. + /// + /// # Arguments + /// - `session`: Information about the current session. + /// - `distribution`: Offline information about the distribution. + /// + /// # Returns + /// - `Ok(())`: If the plugin successfully handled the event. + /// - `Err(WinError)`: If the event handling failed. /// - /// Returning failure will NOT cause the operation to fail. - /// Introduced in 2.1.2 + /// # Notes + /// - Introduced in API version 2.1.2. #[allow(unused_variables)] fn on_distribution_unregistered( &self, diff --git a/wslplugins-rs/src/utils.rs b/wslplugins-rs/src/utils.rs index 3d0d32f..5a4572b 100644 --- a/wslplugins-rs/src/utils.rs +++ b/wslplugins-rs/src/utils.rs @@ -1,3 +1,9 @@ +//! # String Encoding Utilities +//! +//! This module provides utility functions to handle string encoding conversions, specifically for: +//! - Encoding `OsStr` as wide, null-terminated UTF-16 strings. +//! - Creating `CString` instances from Rust strings, filtering out null bytes. + use std::ffi::{CString, OsStr}; use std::os::windows::ffi::OsStrExt; @@ -19,6 +25,7 @@ mod tests { use super::*; use std::ffi::OsString; + /// Tests `encode_wide_null_terminated` with a string containing no null characters. #[test] fn test_encode_wide_null_terminated_no_nulls() { let input = OsString::from("Hello"); @@ -26,6 +33,7 @@ mod tests { assert_eq!(encode_wide_null_terminated(&input), expected); } + /// Tests `encode_wide_null_terminated` with a string containing null characters. #[test] fn test_encode_wide_null_terminated_with_nulls() { let input = OsString::from("Hel\0lo"); @@ -33,6 +41,7 @@ mod tests { assert_eq!(encode_wide_null_terminated(&input), expected); } + /// Tests `cstring_from_str` with a string containing no null characters. #[test] fn test_cstring_from_str_no_nulls() { let input = "Hello"; @@ -40,6 +49,7 @@ mod tests { assert_eq!(cstring.to_str().unwrap(), input); } + /// Tests `cstring_from_str` with a string containing null characters. #[test] fn test_cstring_from_str_with_nulls() { let input = "Hel\0lo"; diff --git a/wslplugins-rs/src/wsl_context.rs b/wslplugins-rs/src/wsl_context.rs index 0a9fb2b..f908a38 100644 --- a/wslplugins-rs/src/wsl_context.rs +++ b/wslplugins-rs/src/wsl_context.rs @@ -1,21 +1,64 @@ +//! # WSL Context Management +//! +//! This module provides a global context for interacting with the WSL plugin API. It ensures +//! that a single instance of `WSLContext` is initialized and accessible globally during the +//! plugin's lifecycle. + use crate::api::ApiV1; use std::sync::OnceLock; +/// A static instance of `WSLContext` initialized once during the program's lifecycle. static CURRENT_CONTEXT: OnceLock = OnceLock::new(); +/// A global context for the WSL plugin API. +/// +/// The `WSLContext` contains the API interface (`ApiV1`) and ensures safe, global access +/// throughout the plugin's lifecycle. pub struct WSLContext { + /// The API interface used for interacting with the WSL plugin API. pub api: ApiV1<'static>, } impl WSLContext { + /// Retrieves the current `WSLContext` instance, if it has been initialized. + /// + /// # Returns + /// - `Some(&'static WSLContext)`: If the context has been initialized. + /// - `None`: If the context has not yet been initialized. + /// + /// # Example + /// ```rust + /// use wslplugins_rs::WSLContext; + /// + /// if let Some(context) = WSLContext::get_current() { + /// // Use the context + /// } else { + /// eprintln!("WSL context is not initialized."); + /// } + /// ``` pub fn get_current() -> Option<&'static WSLContext> { CURRENT_CONTEXT.get() } + /// Retrieves the current `WSLContext` instance or panics if it is not initialized. + /// + /// # Panics + /// This function will panic if the context has not been initialized. + /// + /// # Returns + /// A reference to the current `WSLContext`. pub fn get_current_or_panic() -> &'static WSLContext { Self::get_current().expect("WSL context is not initialised.") } + /// Initializes the global `WSLContext` with the provided `ApiV1` instance. + /// + /// # Arguments + /// - `api`: The [ApiV1] instance to associate with the context. + /// + /// # Returns + /// - `Some(&'static WSLContext)`: If the context was successfully initialized. + /// - `None`: If the context has already been initialized. pub fn init(api: ApiV1<'static>) -> Option<&'static Self> { CURRENT_CONTEXT.set(WSLContext { api }).ok()?; CURRENT_CONTEXT.get() diff --git a/wslplugins-rs/src/wsl_session_information.rs b/wslplugins-rs/src/wsl_session_information.rs index 9f69a55..654bde5 100644 --- a/wslplugins-rs/src/wsl_session_information.rs +++ b/wslplugins-rs/src/wsl_session_information.rs @@ -1,48 +1,90 @@ +//! # WSL Session Information +//! +//! This module provides a safe abstraction over the `WSLSessionInformation` structure +//! from the WSL Plugin API, allowing access to session details in an idiomatic Rust interface. + extern crate wslplugins_sys; use core::hash; use std::fmt; use windows::Win32::Foundation::*; use windows::Win32::Security::PSID; +/// Represents session information for a WSL instance. +/// +/// This struct wraps the `WSLSessionInformation` provided by the WSL Plugin API and +/// provides safe, idiomatic access to its fields. +/// +/// # Lifetime Parameters +/// - `'a`: The lifetime of the referenced `WSLSessionInformation` instance. pub struct WSLSessionInformation<'a>(&'a wslplugins_sys::WSLSessionInformation); -impl hash::Hash for WSLSessionInformation<'_> { - fn hash(&self, state: &mut H) { - self.0.SessionId.hash(state); - } -} - -impl PartialEq for WSLSessionInformation<'_> { - fn eq(&self, other: &Self) -> bool { - self.0.SessionId == other.0.SessionId - } -} - impl WSLSessionInformation<'_> { - // Getter for id + /// Retrieves the session ID. + /// + /// # Returns + /// The unique session ID as a [u32]. pub fn id(&self) -> u32 { self.0.SessionId } - // Getter for user_token + /// Retrieves the user token for the session. + /// + /// # Returns + /// A [HANDLE] representing the user token. pub fn user_token(&self) -> HANDLE { self.0.UserToken } - // Getter for user_sid + /// Retrieves the user SID (security identifier) for the session. + /// + /// # Returns + /// A [PSID] representing the user SID. pub fn user_sid(&self) -> PSID { self.0.UserSid } } impl<'a> From<&'a wslplugins_sys::WSLSessionInformation> for WSLSessionInformation<'a> { + /// Creates a `WSLSessionInformation` instance from a reference to `WSLSessionInformation` from the API. + /// + /// # Arguments + /// - `ptr`: A reference to a `WSLSessionInformation` instance. + /// + /// # Returns + /// A safe wrapper around the provided pointer. fn from(ptr: &'a wslplugins_sys::WSLSessionInformation) -> Self { Self(ptr) } } -// Implement Debug manually +impl hash::Hash for WSLSessionInformation<'_> { + /// Computes a hash based on the session ID. + /// + /// # Arguments + /// - `state`: The hasher state to update with the session ID. + fn hash(&self, state: &mut H) { + self.0.SessionId.hash(state); + } +} + +impl PartialEq for WSLSessionInformation<'_> { + /// Compares two `WSLSessionInformation` instances for equality based on their session IDs. + /// + /// # Arguments + /// - `other`: The other `WSLSessionInformation` instance to compare. + /// + /// # Returns + /// `true` if the session IDs are equal, `false` otherwise. + fn eq(&self, other: &Self) -> bool { + self.0.SessionId == other.0.SessionId + } +} + +// Manually implements Debug for `WSLSessionInformation`. impl fmt::Debug for WSLSessionInformation<'_> { + /// Formats the session information for debugging. + /// + /// The output includes the session ID, user token, and user SID. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WSLSessionInformation") .field("sessionId", &self.0.SessionId) diff --git a/wslplugins-rs/src/wsl_vm_creation_settings.rs b/wslplugins-rs/src/wsl_vm_creation_settings.rs index 688c2b6..2206742 100644 --- a/wslplugins-rs/src/wsl_vm_creation_settings.rs +++ b/wslplugins-rs/src/wsl_vm_creation_settings.rs @@ -1,21 +1,59 @@ -use crate::wsl_user_configuration::WSLUserConfiguration; +//! # WSL VM Creation Settings +//! +//! This module provides a safe abstraction for handling WSL VM creation settings. It wraps the +//! `WSLVmCreationSettings` structure from the WSL Plugin API and provides utilities for accessing +//! custom configuration flags with support for multiple flag management libraries. + use std::fmt::Debug; +use crate::WSLUserConfiguration; + +/// Represents WSL VM creation settings. +/// +/// This struct wraps the `WSLVmCreationSettings` structure from the WSL Plugin API, providing +/// safe and idiomatic Rust access to its fields. +/// +/// # Lifetime Parameters +/// - `'a`: The lifetime of the referenced `WSLVmCreationSettings` instance. pub struct WSLVmCreationSettings<'a>(&'a wslplugins_sys::WSLVmCreationSettings); impl<'a> From<&'a wslplugins_sys::WSLVmCreationSettings> for WSLVmCreationSettings<'a> { + /// Creates a `WSLVmCreationSettings` instance from a reference to the raw WSL Plugin API structure. + /// + /// # Arguments + /// - `value`: A reference to a `WSLVmCreationSettings` instance from the WSL Plugin API. + /// + /// # Returns + /// A wrapped `WSLVmCreationSettings` instance. fn from(value: &'a wslplugins_sys::WSLVmCreationSettings) -> Self { WSLVmCreationSettings(value) } } impl WSLVmCreationSettings<'_> { + /// Retrieves the custom configuration flags for the VM. + /// - **Default**: Uses a basic implementation when no flag library is enabled. + /// - **`bitflags`**: Uses the `bitflags` crate for managing flags. + /// - **`flagset`**: Uses the `flagset` crate for managing flags. + /// - **`enumflags2`**: Uses the `enumflags2` crate for managing flags. + /// + /// # Returns + /// A wrapper type type representing the custom configuration flags. + /// This type is convertible to some flags if the associated feature is enabled + /// /// - **Default**: Uses a basic implementation when no flag library is enabled. + /// - **`bitflags`**: Uses the `bitflags` crate for managing flags. + /// - **`flagset`**: Uses the `flagset` crate for managing flags. + /// - **`enumflags2`**: Uses the `enumflags2` crate for managing flags. + /// pub fn custom_configuration_flags(&self) -> WSLUserConfiguration { WSLUserConfiguration::from(self.0.CustomConfigurationFlags) } } impl Debug for WSLVmCreationSettings<'_> { + /// Formats the VM creation settings for debugging. + /// + /// The debug output includes the custom configuration flags. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("WSLVmCreationSettings") .field( From 670b92bcc9503555773d9030b2612f4a5841ae40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20V=C3=A9ril?= Date: Sun, 5 Jan 2025 13:58:35 +0100 Subject: [PATCH 2/5] Improve documentation for wsl_user_configuration --- wslplugins-rs/src/wsl_user_configuration/bitflags.rs | 2 ++ .../src/wsl_user_configuration/enumflags2.rs | 3 +++ wslplugins-rs/src/wsl_user_configuration/flagset.rs | 2 ++ wslplugins-rs/src/wsl_user_configuration/mod.rs | 3 +++ wslplugins-rs/src/wsl_vm_creation_settings.rs | 11 +++-------- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/wslplugins-rs/src/wsl_user_configuration/bitflags.rs b/wslplugins-rs/src/wsl_user_configuration/bitflags.rs index 1b4d909..bf7b5a0 100644 --- a/wslplugins-rs/src/wsl_user_configuration/bitflags.rs +++ b/wslplugins-rs/src/wsl_user_configuration/bitflags.rs @@ -1,7 +1,9 @@ +//! Provides a [bitflags] implementation for [WSLUserConfiguration] flags. use super::WSLUserConfiguration; use bitflags::bitflags; bitflags! { + /// Represents the user configuration flags for WSL. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct WSLUserConfigurationFlags: i32 { const CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel; diff --git a/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs b/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs index 4fad550..8087204 100644 --- a/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs +++ b/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs @@ -1,8 +1,11 @@ +//! Provides an [enumflags2] implementation for [WSLUserConfiguration] flags. use super::WSLUserConfiguration; use enumflags2::{bitflags, BitFlags}; + #[bitflags] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr(u32)] +/// Represents the user configuration flags for WSL as an enumflags2 bitflags. pub enum WSLUserConfigurationFlags { CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel as u32, CustomKernelCommandLine = diff --git a/wslplugins-rs/src/wsl_user_configuration/flagset.rs b/wslplugins-rs/src/wsl_user_configuration/flagset.rs index 539d440..f11064f 100644 --- a/wslplugins-rs/src/wsl_user_configuration/flagset.rs +++ b/wslplugins-rs/src/wsl_user_configuration/flagset.rs @@ -1,7 +1,9 @@ +//! Provides an [flagset] implementation for [WSLUserConfiguration] flags. use super::WSLUserConfiguration; use flagset::{flags, FlagSet}; flags! { + /// Represents the user configuration flags for WSL as flagset flags. #[derive(Hash)] pub enum WSLUserConfigurationFlags: i32 { CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel, diff --git a/wslplugins-rs/src/wsl_user_configuration/mod.rs b/wslplugins-rs/src/wsl_user_configuration/mod.rs index 1d80c5d..5fc10fa 100644 --- a/wslplugins-rs/src/wsl_user_configuration/mod.rs +++ b/wslplugins-rs/src/wsl_user_configuration/mod.rs @@ -1,3 +1,5 @@ +//! Provide types to interact with WSLUserConfiguration. + #[cfg(feature = "bitflags")] pub mod bitflags; @@ -7,6 +9,7 @@ pub mod enumflags2; #[cfg(feature = "flagset")] pub mod flagset; +/// Represents a WSL user configuration as an integer. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct WSLUserConfiguration(i32); diff --git a/wslplugins-rs/src/wsl_vm_creation_settings.rs b/wslplugins-rs/src/wsl_vm_creation_settings.rs index 2206742..34c080b 100644 --- a/wslplugins-rs/src/wsl_vm_creation_settings.rs +++ b/wslplugins-rs/src/wsl_vm_creation_settings.rs @@ -32,18 +32,13 @@ impl<'a> From<&'a wslplugins_sys::WSLVmCreationSettings> for WSLVmCreationSettin impl WSLVmCreationSettings<'_> { /// Retrieves the custom configuration flags for the VM. - /// - **Default**: Uses a basic implementation when no flag library is enabled. - /// - **`bitflags`**: Uses the `bitflags` crate for managing flags. - /// - **`flagset`**: Uses the `flagset` crate for managing flags. - /// - **`enumflags2`**: Uses the `enumflags2` crate for managing flags. /// /// # Returns /// A wrapper type type representing the custom configuration flags. /// This type is convertible to some flags if the associated feature is enabled - /// /// - **Default**: Uses a basic implementation when no flag library is enabled. - /// - **`bitflags`**: Uses the `bitflags` crate for managing flags. - /// - **`flagset`**: Uses the `flagset` crate for managing flags. - /// - **`enumflags2`**: Uses the `enumflags2` crate for managing flags. + /// - **`bitflags`**: Uses the [bitflags] crate for managing flags. + /// - **`flagset`**: Uses the [flagset] crate for managing flags. + /// - **`enumflags2`**: Uses the [enumflags2] crate for managing flags. /// pub fn custom_configuration_flags(&self) -> WSLUserConfiguration { WSLUserConfiguration::from(self.0.CustomConfigurationFlags) From e2a8805333aadf40e4e1515e13988a4e9e604b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20V=C3=A9ril?= Date: Sun, 5 Jan 2025 14:10:11 +0100 Subject: [PATCH 3/5] Fix check --- wslplugins-rs/src/distribution_information.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wslplugins-rs/src/distribution_information.rs b/wslplugins-rs/src/distribution_information.rs index 2d2b474..e3c0e47 100644 --- a/wslplugins-rs/src/distribution_information.rs +++ b/wslplugins-rs/src/distribution_information.rs @@ -13,9 +13,10 @@ //! - PID namespace extern crate wslplugins_sys; +#[cfg(doc)] +use crate::api::errors::require_update_error::Error; use crate::api::{ - errors::require_update_error::{Error, Result}, - utils::check_required_version_result_from_context, + errors::require_update_error::Result, utils::check_required_version_result_from_context, }; use crate::core_distribution_information::CoreDistributionInformation; use crate::WSLContext; @@ -53,7 +54,8 @@ impl DistributionInformation<'_> { /// /// # Returns /// - `Ok(u32)`: The PID of the init process. - /// - `Err(Error)`: If the API version is insufficient. + /// # Errors + /// [Error]: If the API version is insufficient. pub fn init_pid(&self) -> Result { check_required_version_result_from_context( WSLContext::get_current_or_panic(), From b9c2e2a35d7c6e2bac84315a3b996f284b3cd5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20V=C3=A9ril?= Date: Mon, 6 Jan 2025 18:02:26 +0100 Subject: [PATCH 4/5] Fix clippy --- wslplugins-rs/src/api/api_v1.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wslplugins-rs/src/api/api_v1.rs b/wslplugins-rs/src/api/api_v1.rs index 68a1e53..93a9809 100644 --- a/wslplugins-rs/src/api/api_v1.rs +++ b/wslplugins-rs/src/api/api_v1.rs @@ -56,9 +56,8 @@ impl ApiV1<'_> { pub fn version(&self) -> &WSLVersion { &self.0.Version } - /// Create plan9 mount between Windows & Linux - /// + /// Create plan9 mount between Windows & Linux /// Allows sharing a folder between the Windows host and the Linux environment. /// /// # Arguments From 5da15f36cb2ccc6b1351b0b4aae4762dd5ef5532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20V=C3=A9ril?= Date: Mon, 6 Jan 2025 19:56:43 +0100 Subject: [PATCH 5/5] Continue to imporove documentation and force documenting public API --- README.md | 2 +- wslplugins-macro-core/src/lib.rs | 8 +++--- wslplugins-rs/src/lib.rs | 6 +++-- wslplugins-rs/src/plugin/utils.rs | 25 +++++++++++++++++++ .../src/wsl_user_configuration/bitflags.rs | 18 ++++++++++++- .../src/wsl_user_configuration/enumflags2.rs | 20 ++++++++++++++- .../src/wsl_user_configuration/flagset.rs | 18 ++++++++++++- .../src/wsl_user_configuration/mod.rs | 19 +++++++++++++- 8 files changed, 105 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 98e2d1f..92967a5 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Ensure you have the following requirements installed: - Cargo (Rust's package manager) - PowerShell (for running signing scripts) - OpenSSL (used in the signing process) [Download OpenSSL](https://slproweb.com/products/Win32OpenSSL.html) -- **SignTool.exe** from the Windows SDK (for signing the plugin) [Download Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/) +- **SignTool.exe** from the Windows SDK (for signing the plugin) [Download Windows SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk/) - nuget.exe (for downloading [Microsoft's WSL Plugin API](https://www.nuget.org/packages/Microsoft.WSL.PluginApi)) ### Important Notes diff --git a/wslplugins-macro-core/src/lib.rs b/wslplugins-macro-core/src/lib.rs index 1535248..ef35688 100644 --- a/wslplugins-macro-core/src/lib.rs +++ b/wslplugins-macro-core/src/lib.rs @@ -1,7 +1,7 @@ -pub(crate) mod generator; -pub(crate) mod hooks; -pub(crate) mod parser; -pub(crate) mod utils; +mod generator; +mod hooks; +mod parser; +mod utils; use generator::generate; use proc_macro2::TokenStream; diff --git a/wslplugins-rs/src/lib.rs b/wslplugins-rs/src/lib.rs index 327902b..0133596 100644 --- a/wslplugins-rs/src/lib.rs +++ b/wslplugins-rs/src/lib.rs @@ -1,3 +1,7 @@ +#![warn(missing_docs)] +// Enable doc_cfg if docrs +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + //! # WSLPlugin-rs //! //! This is the main entry point for the **WSLPlugin-rs** crate, a framework designed for creating @@ -31,8 +35,6 @@ //! } //! } //! ``` -// Enable doc_cfg if docrs -#![cfg_attr(docsrs, feature(doc_auto_cfg))] /// Provides interfaces for interacting with WSL plugin APIs. pub mod api; diff --git a/wslplugins-rs/src/plugin/utils.rs b/wslplugins-rs/src/plugin/utils.rs index 01d472c..882d714 100644 --- a/wslplugins-rs/src/plugin/utils.rs +++ b/wslplugins-rs/src/plugin/utils.rs @@ -11,6 +11,8 @@ use wslplugins_sys::WSLPluginAPIV1; use crate::{api::ApiV1, WSLContext}; +#[cfg(doc)] +use super::Error; use super::{Result, WSLPluginV1}; /// Creates a WSL plugin instance with a specified required API version. @@ -56,6 +58,29 @@ pub fn create_plugin_with_required_version( } } +/// Converts a generic `Result` using the custom `Error` type into a `WinResult`. +/// +/// This function simplifies the interoperability between the custom error handling +/// in the WSL plugin system and the Windows error system by mapping the plugin [Error] +/// into a [windows::core::Error] using the `consume_error_message_unwrap` method. +/// +/// # Arguments +/// - `result`: A [Result] using the custom [Error] type defined in this crate. +/// +/// # Returns +/// A `WinResult` where: +/// - `Ok(value)` contains the successful result `T`. +/// - `Err(error)` contains a [windows::core::Error] converted from the plugin [Error]. +/// +/// # Behavior +/// - If the `result` is `Ok`, it is returned as-is. +/// - If the `result` is `Err`, the error is consumed using the +/// and sent to WSL and is then +/// converted into a [windows::core::Error]. +/// +/// # Usage +/// This utility is intended to facilitate the transition between idiomatic Rust +/// error handling and the Windows API error-handling conventions. pub fn consume_to_win_result(result: Result) -> WinResult { result.map_err(|err| err.consume_error_message_unwrap()) } diff --git a/wslplugins-rs/src/wsl_user_configuration/bitflags.rs b/wslplugins-rs/src/wsl_user_configuration/bitflags.rs index bf7b5a0..32c89ec 100644 --- a/wslplugins-rs/src/wsl_user_configuration/bitflags.rs +++ b/wslplugins-rs/src/wsl_user_configuration/bitflags.rs @@ -3,10 +3,26 @@ use super::WSLUserConfiguration; use bitflags::bitflags; bitflags! { - /// Represents the user configuration flags for WSL. + /// Represents the user configuration flags for Windows Subsystem for Linux (WSL) as + /// [bitflags] + /// + /// These flags are used to customize the behavior of WSL instances based on user configuration. + /// The values correspond to the definitions in the WSL Plugin API provided by Microsoft. + /// + /// # Variants + /// + /// - `CustomKernel`: Indicates that the WSL instance use use a custom Linux kernel instead of the default kernel. + /// - `CustomKernelCommandLine`: Specifies that the WSL instance use use a custom kernel command-line during boot. + /// + /// # References + /// + /// See [WSL Configuration](https://learn.microsoft.com/windows/wsl/wsl-config) + /// for additional details on WSL user configurations. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct WSLUserConfigurationFlags: i32 { + /// A custom Linux kernel is used for the WSL instance. const CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel; + /// A custom kernel command-line is used for the WSL instance. const CustomKernelCommandLine = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernelCommandLine; } } diff --git a/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs b/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs index 8087204..327ed4b 100644 --- a/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs +++ b/wslplugins-rs/src/wsl_user_configuration/enumflags2.rs @@ -2,12 +2,30 @@ use super::WSLUserConfiguration; use enumflags2::{bitflags, BitFlags}; +/// Represents the user configuration flags for Windows Subsystem for Linux (WSL) as +/// [enumflags2] +/// +/// These flags are used to customize the behavior of WSL instances based on user configuration. +/// The values correspond to the definitions in the WSL Plugin API provided by Microsoft. +/// +/// # Variants +/// +/// - `CustomKernel`: Indicates that the WSL instance use use a custom Linux kernel instead of the default kernel. +/// - `CustomKernelCommandLine`: Specifies that the WSL instance use use a custom kernel command-line during boot. +/// +/// +/// # References +/// +/// See [WSL Configuration](https://learn.microsoft.com/windows/wsl/wsl-config) +/// for additional details on WSL user configurations. #[bitflags] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr(u32)] -/// Represents the user configuration flags for WSL as an enumflags2 bitflags. pub enum WSLUserConfigurationFlags { + /// A custom Linux kernel is used for the WSL instance. CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel as u32, + + /// A custom kernel command-line is used for the WSL instance. CustomKernelCommandLine = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernelCommandLine as u32, } diff --git a/wslplugins-rs/src/wsl_user_configuration/flagset.rs b/wslplugins-rs/src/wsl_user_configuration/flagset.rs index f11064f..6759a25 100644 --- a/wslplugins-rs/src/wsl_user_configuration/flagset.rs +++ b/wslplugins-rs/src/wsl_user_configuration/flagset.rs @@ -3,10 +3,26 @@ use super::WSLUserConfiguration; use flagset::{flags, FlagSet}; flags! { - /// Represents the user configuration flags for WSL as flagset flags. + /// Represents the user configuration flags for Windows Subsystem for Linux (WSL) as + /// [flagset] + /// + /// These flags are used to customize the behavior of WSL instances based on user configuration. + /// The values correspond to the definitions in the WSL Plugin API provided by Microsoft. + /// + /// # Variants + /// + /// - `CustomKernel`: Indicates that the WSL instance use use a custom Linux kernel instead of the default kernel. + /// - `CustomKernelCommandLine`: Specifies that the WSL instance use use a custom kernel command-line during boot. + /// + /// # References + /// + /// See [WSL Configuration](https://learn.microsoft.com/windows/wsl/wsl-config) + /// for additional details on WSL user configurations. #[derive(Hash)] pub enum WSLUserConfigurationFlags: i32 { + /// A custom Linux kernel is used for the WSL instance. CustomKernel = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernel, + /// A custom kernel command-line is used for the WSL instance. CustomKernelCommandLine = wslplugins_sys::WSLUserConfiguration_WSLUserConfigurationCustomKernelCommandLine, } diff --git a/wslplugins-rs/src/wsl_user_configuration/mod.rs b/wslplugins-rs/src/wsl_user_configuration/mod.rs index 5fc10fa..adcb7c7 100644 --- a/wslplugins-rs/src/wsl_user_configuration/mod.rs +++ b/wslplugins-rs/src/wsl_user_configuration/mod.rs @@ -1,4 +1,18 @@ -//! Provide types to interact with WSLUserConfiguration. +//! Provides types and utilities to interact with WSL (Windows Subsystem for Linux) [user configurations](https://learn.microsoft.com/windows/wsl/wsl-config). +//! +//! This module defines the `WSLUserConfiguration` struct for representing user configurations as an integer, +//! along with optional submodules for flag handling depending on feature flags. +//! +//! +//! # References +//! +//! See [WSL Configuration](https://learn.microsoft.com/windows/wsl/wsl-config) +//! for additional details on WSL user configurations. +//! # Features +//! +//! - **`bitflags`**: Provides a [bitflags]-based implementation for handling user configuration flags. +//! - **`enumflags2`**: Provides an [enumflags2]-based implementation for handling user configuration flags. +//! - **`flagset`**: Provides a [flagset]-based implementation for handling user configuration flags. #[cfg(feature = "bitflags")] pub mod bitflags; @@ -10,6 +24,9 @@ pub mod enumflags2; pub mod flagset; /// Represents a WSL user configuration as an integer. +/// +/// This struct provides a simple wrapper around a 32-bit integer ([i32]), allowing for +/// easy conversion to and from [i32] values and also flags depending on the enabled feature. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct WSLUserConfiguration(i32);