Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minimal documentation to CGP framework #46

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
/target
/target

# These are backup files generated by rustfmt
**/*.rs.bk

# Jetbrains files & folders
.idea
.idea/
.idea/*.*

# Visual Studio Code stuff
.vscode

# Apple stuff
.DS_Store
*/.DS_Store

# windsurf rules
.windsurfrules
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