Skip to content

Commit

Permalink
docs(cgp-component-macro-lib): add comprehensive documentation for ma…
Browse files Browse the repository at this point in the history
…cro components

Addresses issue:
contextgeneric#45

Add detailed Rust docstrings to core macro implementation files, improving code
understanding and maintainability. Documentation follows Rust best practices with
clear examples and detailed explanations.

Each file's documentation includes:
- Clear overview and purpose
- Detailed function/struct documentation
- Arguments and return value descriptions
- Practical code examples
- Implementation notes and edge cases
- Type parameter explanations where applicable

All examples are marked with `ignore` where they depend on the full CGP framework
context. Documentation has been verified with `cargo test --doc` and `cargo doc`.

Part of the ongoing effort to improve CGP framework documentation and usability.

 Signed-off-by: Marvin Hansen <[email protected]>
  • Loading branch information
marvin-hansen committed Dec 29, 2024
1 parent 2a20479 commit 1cafd62
Show file tree
Hide file tree
Showing 28 changed files with 1,069 additions and 0 deletions.
79 changes: 79 additions & 0 deletions crates/cgp-component-macro-lib/src/delegate_components/ast.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/// Abstract Syntax Tree (AST) structures for component delegation.
///
/// This module provides the AST representation for parsing and processing
/// component delegation syntax in the CGP framework.
use core::iter;

use proc_macro2::TokenStream;
Expand All @@ -7,28 +12,70 @@ use syn::punctuated::Punctuated;
use syn::token::{Bracket, Colon, Comma, Lt};
use syn::{braced, bracketed, Generics, Token, Type};

/// Root AST node representing a complete component delegation specification.
///
/// This structure captures the target type that will implement the delegated
/// components, its generic parameters, and the entries specifying which
/// components are delegated to which sources.
///
/// # Fields
///
/// * `target_type` - The type that will implement the delegated components
/// * `target_generics` - Generic parameters for the target type
/// * `delegate_entries` - Collection of delegation specifications
pub struct DelegateComponentsAst {
pub target_type: Type,
pub target_generics: Generics,
pub delegate_entries: DelegateEntriesAst,
}

/// Collection of delegate entries in the AST.
///
/// Represents a comma-separated list of delegate entries, where each entry
/// specifies which components are delegated to a particular source.
///
/// # Fields
///
/// * `entries` - Punctuated sequence of delegate entries
pub struct DelegateEntriesAst {
pub entries: Punctuated<DelegateEntryAst, Comma>,
}

/// Single delegation entry in the AST.
///
/// Specifies a mapping between a set of components and their source type
/// for delegation.
///
/// # Fields
///
/// * `components` - List of components to be delegated
/// * `source` - The type that provides the component implementations
pub struct DelegateEntryAst {
pub components: Punctuated<ComponentAst, Comma>,
pub source: Type,
}

/// AST node representing a single component specification.
///
/// Captures a component type and its associated generic parameters.
///
/// # Fields
///
/// * `component_type` - The type of the component being delegated
/// * `component_generics` - Generic parameters for the component
#[derive(Clone)]
pub struct ComponentAst {
pub component_type: Type,
pub component_generics: Generics,
}

impl DelegateEntriesAst {
/// Collects all components from all delegate entries into a single sequence.
///
/// # Returns
///
/// Returns a `Punctuated` sequence containing all components across all entries,
/// preserving their order of appearance.
pub fn all_components(&self) -> Punctuated<ComponentAst, Comma> {
self.entries
.iter()
Expand All @@ -37,6 +84,12 @@ impl DelegateEntriesAst {
}
}

/// Parse implementation for delegate components AST.
///
/// Parses input in the format:
/// ```text
/// <generics>? target_type { entries... }
/// ```
impl Parse for DelegateComponentsAst {
fn parse(input: ParseStream) -> syn::Result<Self> {
let target_generics = if input.peek(Lt) {
Expand All @@ -57,6 +110,12 @@ impl Parse for DelegateComponentsAst {
}
}

/// Parse implementation for delegate entries.
///
/// Parses input in the format:
/// ```text
/// { entry1, entry2, ... }
/// ```
impl Parse for DelegateEntriesAst {
fn parse(input: ParseStream) -> syn::Result<Self> {
let entries = {
Expand All @@ -69,6 +128,16 @@ impl Parse for DelegateEntriesAst {
}
}

/// Parse implementation for a single delegate entry.
///
/// Parses input in the format:
/// ```text
/// [comp1, comp2, ...]: source_type
/// ```
/// or
/// ```text
/// comp: source_type
/// ```
impl Parse for DelegateEntryAst {
fn parse(input: ParseStream) -> syn::Result<Self> {
let components = if input.peek(Bracket) {
Expand All @@ -88,6 +157,12 @@ impl Parse for DelegateEntryAst {
}
}

/// Parse implementation for component specification.
///
/// Parses input in the format:
/// ```text
/// <generics>? component_type
/// ```
impl Parse for ComponentAst {
fn parse(input: ParseStream) -> syn::Result<Self> {
let component_generics = if input.peek(Lt) {
Expand All @@ -105,6 +180,10 @@ impl Parse for ComponentAst {
}
}

/// Token stream generation for component specifications.
///
/// Converts a component specification into a token stream by concatenating
/// its generic parameters and component type.
impl ToTokens for ComponentAst {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(self.component_generics.to_token_stream());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,53 @@ use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{parse_quote, GenericParam, Generics, Ident, ItemStruct, Type};

/// Generates a struct definition with proper handling of generic parameters and lifetimes.
///
/// This function creates a new struct definition based on the provided identifier and generic parameters.
/// It handles two cases:
/// - For structs without generic parameters: Creates a simple unit struct
/// - For structs with generic parameters: Creates a struct with a `PhantomData` field that properly
/// captures all type parameters and lifetimes
///
/// The function ensures proper type safety by:
/// - Removing bounds from type parameters to avoid unnecessary constraints
/// - Converting lifetime parameters into reference types with appropriate lifetimes
/// - Using `PhantomData` to maintain variance without adding runtime overhead
///
/// # Arguments
///
/// * `ident` - The identifier for the new struct
/// * `generics` - The generic parameters specification, which may include type parameters,
/// lifetime parameters, and const generics
///
/// # Returns
///
/// Returns a [`syn::ItemStruct`] representing the complete struct definition with all
/// necessary generic parameters and phantom data fields.
///
/// # Examples
///
/// ```rust
/// # use syn::{parse_quote, Generics, Ident};
/// # use cgp_component_macro_lib::delegate_components::define_struct::define_struct;
/// // Simple struct without generics
/// let simple_ident: Ident = parse_quote!(SimpleStruct);
/// let empty_generics: Generics = parse_quote!();
/// let simple = define_struct(&simple_ident, &empty_generics);
/// // Results in: pub struct SimpleStruct;
///
/// // Generic struct with type parameter and lifetime
/// let generic_ident: Ident = parse_quote!(GenericStruct);
/// let generics: Generics = parse_quote!(<T, 'a>);
/// let generic = define_struct(&generic_ident, &generics);
/// // Results in: pub struct GenericStruct<T, 'a>(pub ::core::marker::PhantomData<(T, &'a ())>);
/// ```
///
/// # Note
///
/// The generated struct will always be public (`pub`) and will use `PhantomData`
/// to properly handle generic parameters without introducing runtime overhead.
pub fn define_struct(ident: &Ident, generics: &Generics) -> ItemStruct {
if generics.params.is_empty() {
parse_quote! {
Expand Down
17 changes: 17 additions & 0 deletions crates/cgp-component-macro-lib/src/delegate_components/delegate.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
/// Imports required for token stream processing and AST manipulation
use proc_macro2::TokenStream;
use quote::ToTokens;

use crate::delegate_components::ast::DelegateComponentsAst;
use crate::delegate_components::impl_delegate::impl_delegate_components;

/// Processes component delegation macro by generating the necessary trait implementations.
///
/// # Arguments
/// * `body` - The input token stream containing the delegation specification
///
/// # Returns
/// * `syn::Result<TokenStream>` - The generated implementation code as a token stream
///
/// # Example
/// This function handles macro invocations like:
/// ```ignore
/// #[delegate_components]
/// struct MyStruct {
/// delegate: DelegateType,
/// }
/// ```
pub fn delegate_components(body: TokenStream) -> syn::Result<TokenStream> {
let ast: DelegateComponentsAst = syn::parse2(body)?;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
/// Functionality for defining delegate component trait bounds and trait implementations
use syn::punctuated::Punctuated;
use syn::token::Plus;
use syn::{parse_quote, Generics, Ident, ItemImpl, ItemTrait, Type, TypeParamBound};

use crate::delegate_components::ast::DelegateEntriesAst;

/// Generates trait bounds for delegated components.
///
/// This function creates a set of trait bounds that ensure each component
/// can be delegated to the target type. It processes all components in the
/// delegate entries and creates appropriate DelegateComponent bounds.
///
/// # Arguments
/// * `target_type` - The type that components will delegate to
/// * `delegate_entries` - AST containing all component delegation specifications
///
/// # Returns
/// * `Punctuated<TypeParamBound, Plus>` - A sequence of trait bounds separated by '+'
///
/// # Example
/// For a component of type `MyComponent` delegating to `TargetType`, generates:
/// ```ignore
/// DelegateComponent<MyComponent, Delegate = TargetType>
/// ```
pub fn define_delegate_component_trait_bounds(
target_type: &Type,
delegate_entries: &DelegateEntriesAst,
Expand All @@ -21,6 +40,30 @@ pub fn define_delegate_component_trait_bounds(
trait_bounds
}

/// Defines a trait and its implementation for component delegation.
///
/// This function creates:
/// 1. A trait with bounds ensuring all components can delegate to the target type
/// 2. A generic implementation of this trait for any type meeting these bounds
///
/// # Arguments
/// * `trait_name` - Name of the trait to define
/// * `target_type` - The type that components will delegate to
/// * `target_generics` - Generic parameters for the target type
/// * `delegate_entries` - AST containing all component delegation specifications
///
/// # Returns
/// * `(ItemTrait, ItemImpl)` - Tuple containing the trait definition and its implementation
///
/// # Example
/// For trait name `DelegatesTo`, generates something like:
/// ```ignore
/// pub trait DelegatesTo<T>: DelegateComponent<Component1> + DelegateComponent<Component2> {}
/// impl<T, Components> DelegatesTo<T> for Components
/// where
/// Components: DelegateComponent<Component1> + DelegateComponent<Component2>
/// {}
/// ```
pub fn define_delegates_to_trait(
trait_name: &Ident,
target_type: &Type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ use syn::{parse_quote, Generics, ImplItem, ImplItemType, ItemImpl, Path, Type};
use crate::delegate_components::ast::{ComponentAst, DelegateEntriesAst};
use crate::delegate_components::merge_generics::merge_generics;

/// Generates implementation blocks for delegated components.
///
/// This function creates the necessary trait implementations for each component
/// that is being delegated to another type.
///
/// # Arguments
/// * `target_type` - The type that is delegating its components
/// * `target_generics` - Generic parameters of the target type
/// * `delegate_entries` - AST nodes describing the delegation relationships
///
/// # Returns
/// A vector of implementation blocks for each delegated component
pub fn impl_delegate_components(
target_type: &Type,
target_generics: &Generics,
Expand All @@ -21,6 +33,22 @@ pub fn impl_delegate_components(
.collect()
}

/// Creates a single implementation block for a delegated component.
///
/// # Arguments
/// * `target_type` - The type implementing the delegation
/// * `target_generics` - Generic parameters of the target type
/// * `component` - AST node describing the component being delegated
/// * `source` - The type that provides the component implementation
///
/// # Returns
/// An implementation block (ItemImpl) that defines the delegation relationship
///
/// # Implementation Details
/// This function:
/// 1. Constructs the DelegateComponent trait path
/// 2. Defines the associated Delegate type
/// 3. Merges generics from both the target and component
pub fn impl_delegate_component(
target_type: &Type,
target_generics: &Generics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,41 @@ use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{Generics, WhereClause, WherePredicate};

/// Merges two sets of generic parameters into a single unified set.
///
/// This function combines generic parameters and where clauses from two different
/// generic specifications into a single coherent set. It preserves all type parameters,
/// lifetime parameters, and where clause predicates from both inputs.
///
/// # Arguments
///
/// * `generics_a` - First set of generic parameters to merge
/// * `generics_b` - Second set of generic parameters to merge
///
/// # Returns
///
/// Returns a new [`syn::Generics`] instance containing:
/// - Combined generic parameters from both inputs
/// - Merged where clauses (if any exist in either input)
/// - Angle bracket tokens from the first input (`generics_a`)
///
/// # Examples
///
/// ```rust
/// # use syn::{parse_quote, Generics};
/// # use cgp_component_macro_lib::delegate_components::merge_generics::merge_generics;
/// # use std::fmt::{Debug, Display};
/// let generics_a: Generics = parse_quote!(<T: Debug>);
/// let generics_b: Generics = parse_quote!(<U: Display>);
/// let merged = merge_generics(&generics_a, &generics_b);
/// // Results in: <T: Debug, U: Display>
/// ```
///
/// # Note
///
/// The function preserves the angle bracket tokens (`lt_token` and `gt_token`) from
/// the first set of generics (`generics_a`). This is typically desirable as these
/// tokens usually carry the same span information throughout a macro expansion.
pub fn merge_generics(generics_a: &Generics, generics_b: &Generics) -> Generics {
let mut params = generics_a.params.clone();
params.extend(generics_b.params.clone());
Expand Down
Loading

0 comments on commit 1cafd62

Please sign in to comment.