blocks 0.1.0

A high-performance Rust library for block-based content editing with JSON, Markdown, and HTML support
Documentation
/// Plugin system for extending blocks functionality
///
/// Allows developers to create custom processors and transformers for blocks.
use crate::block::Block;
use crate::document::Document;
use crate::error::Result;
use std::fmt;

/// Trait for block processors/transformers
///
/// Plugins can implement this trait to add custom behavior
pub trait BlockProcessor: Send + Sync {
    /// Returns the name of the processor
    fn name(&self) -> &str;

    /// Returns a description of what the processor does
    fn description(&self) -> &str;

    /// Processes a single block
    /// Return true if the block was modified, false otherwise
    fn process_block(&self, block: &mut Block) -> Result<bool>;

    /// Optional: Process an entire document
    fn process_document(&self, _document: &mut Document) -> Result<()> {
        Ok(())
    }
}

/// Trait for block validators (checks block validity)
pub trait BlockValidator: Send + Sync {
    /// Returns the name of the validator
    fn name(&self) -> &str;

    /// Validates a block
    /// Returns Ok(()) if valid, Err otherwise
    fn validate_block(&self, block: &Block) -> Result<()>;
}

/// Trait for content transformers (e.g., markdown to HTML)
pub trait ContentTransformer: Send + Sync {
    /// Returns the name of the transformer
    fn name(&self) -> &str;

    /// Transforms content from one format to another
    fn transform(&self, content: &str) -> Result<String>;
}

/// Plugin lifecycle hooks
pub trait PluginLifecycle {
    /// Called when the plugin is initialized
    fn on_init(&mut self) -> Result<()> {
        Ok(())
    }

    /// Called when the plugin is being unloaded
    fn on_unload(&mut self) -> Result<()> {
        Ok(())
    }
}

/// Plugin registry for managing multiple plugins
pub struct PluginRegistry {
    processors: Vec<Box<dyn BlockProcessor>>,
    validators: Vec<Box<dyn BlockValidator>>,
    transformers: Vec<Box<dyn ContentTransformer>>,
}

impl PluginRegistry {
    /// Creates a new plugin registry
    pub fn new() -> Self {
        Self {
            processors: Vec::new(),
            validators: Vec::new(),
            transformers: Vec::new(),
        }
    }

    /// Registers a block processor
    pub fn register_processor(&mut self, processor: Box<dyn BlockProcessor>) {
        self.processors.push(processor);
    }

    /// Registers a block validator
    pub fn register_validator(&mut self, validator: Box<dyn BlockValidator>) {
        self.validators.push(validator);
    }

    /// Registers a content transformer
    pub fn register_transformer(&mut self, transformer: Box<dyn ContentTransformer>) {
        self.transformers.push(transformer);
    }

    /// Processes a block through all registered processors
    pub fn process_block(&self, block: &mut Block) -> Result<()> {
        for processor in &self.processors {
            processor.process_block(block)?;
        }
        Ok(())
    }

    /// Processes a document through all registered processors
    pub fn process_document(&self, document: &mut Document) -> Result<()> {
        for processor in &self.processors {
            processor.process_document(document)?;
        }
        Ok(())
    }

    /// Validates a block through all registered validators
    pub fn validate_block(&self, block: &Block) -> Result<()> {
        for validator in &self.validators {
            validator.validate_block(block)?;
        }
        Ok(())
    }

    /// Transforms content through all registered transformers
    pub fn transform_content(&self, content: &str) -> Result<String> {
        let mut result = content.to_string();

        for transformer in &self.transformers {
            result = transformer.transform(&result)?;
        }

        Ok(result)
    }

    /// Returns the number of registered processors
    pub fn processor_count(&self) -> usize {
        self.processors.len()
    }

    /// Returns the number of registered validators
    pub fn validator_count(&self) -> usize {
        self.validators.len()
    }

    /// Returns the number of registered transformers
    pub fn transformer_count(&self) -> usize {
        self.transformers.len()
    }

    /// Gets a list of processor names
    pub fn processor_names(&self) -> Vec<&str> {
        self.processors.iter().map(|p| p.name()).collect()
    }

    /// Gets a list of validator names
    pub fn validator_names(&self) -> Vec<&str> {
        self.validators.iter().map(|v| v.name()).collect()
    }

    /// Gets a list of transformer names
    pub fn transformer_names(&self) -> Vec<&str> {
        self.transformers.iter().map(|t| t.name()).collect()
    }

    /// Clears all registered plugins
    pub fn clear(&mut self) {
        self.processors.clear();
        self.validators.clear();
        self.transformers.clear();
    }
}

impl Default for PluginRegistry {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Debug for PluginRegistry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("PluginRegistry")
            .field("processor_count", &self.processor_count())
            .field("validator_count", &self.validator_count())
            .field("transformer_count", &self.transformer_count())
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::BlockType;

    /// Mock processor for testing
    struct MockProcessor;

    impl BlockProcessor for MockProcessor {
        fn name(&self) -> &str {
            "MockProcessor"
        }

        fn description(&self) -> &str {
            "A mock processor for testing"
        }

        fn process_block(&self, block: &mut Block) -> Result<bool> {
            block.content = format!("[PROCESSED] {}", block.content);
            Ok(true)
        }
    }

    /// Mock validator for testing
    struct MockValidator;

    impl BlockValidator for MockValidator {
        fn name(&self) -> &str {
            "MockValidator"
        }

        fn validate_block(&self, block: &Block) -> Result<()> {
            if block.content.is_empty() {
                Err(crate::error::BlocksError::EmptyContent {
                    block_type: "Mock".to_string(),
                })
            } else {
                Ok(())
            }
        }
    }

    /// Mock transformer for testing
    struct MockTransformer;

    impl ContentTransformer for MockTransformer {
        fn name(&self) -> &str {
            "MockTransformer"
        }

        fn transform(&self, content: &str) -> Result<String> {
            Ok(content.to_uppercase())
        }
    }

    #[test]
    fn test_register_processor() {
        let mut registry = PluginRegistry::new();
        assert_eq!(registry.processor_count(), 0);

        registry.register_processor(Box::new(MockProcessor));
        assert_eq!(registry.processor_count(), 1);
        assert_eq!(registry.processor_names(), vec!["MockProcessor"]);
    }

    #[test]
    fn test_register_validator() {
        let mut registry = PluginRegistry::new();
        assert_eq!(registry.validator_count(), 0);

        registry.register_validator(Box::new(MockValidator));
        assert_eq!(registry.validator_count(), 1);
        assert_eq!(registry.validator_names(), vec!["MockValidator"]);
    }

    #[test]
    fn test_register_transformer() {
        let mut registry = PluginRegistry::new();
        assert_eq!(registry.transformer_count(), 0);

        registry.register_transformer(Box::new(MockTransformer));
        assert_eq!(registry.transformer_count(), 1);
        assert_eq!(registry.transformer_names(), vec!["MockTransformer"]);
    }

    #[test]
    fn test_process_block() {
        let mut reg = PluginRegistry::new();
        reg.register_processor(Box::new(MockProcessor));

        let mut block = Block::new(BlockType::Text, "test".to_string());
        let original = block.content.clone();

        reg.process_block(&mut block).unwrap();
        assert!(block.content.contains("[PROCESSED]"));
        assert!(block.content.contains(&original));
    }

    #[test]
    fn test_validate_block() {
        let mut registry = PluginRegistry::new();
        registry.register_validator(Box::new(MockValidator));

        let block = Block::new(BlockType::Text, "test".to_string());
        assert!(registry.validate_block(&block).is_ok());

        let empty_block = Block::new(BlockType::Text, "".to_string());
        assert!(registry.validate_block(&empty_block).is_err());
    }

    #[test]
    fn test_transform_content() {
        let mut registry = PluginRegistry::new();
        registry.register_transformer(Box::new(MockTransformer));

        let result = registry.transform_content("hello").unwrap();
        assert_eq!(result, "HELLO");
    }

    #[test]
    fn test_clear_registry() {
        let mut registry = PluginRegistry::new();
        registry.register_processor(Box::new(MockProcessor));
        registry.register_validator(Box::new(MockValidator));
        registry.register_transformer(Box::new(MockTransformer));

        assert!(registry.processor_count() > 0);
        registry.clear();
        assert_eq!(registry.processor_count(), 0);
        assert_eq!(registry.validator_count(), 0);
        assert_eq!(registry.transformer_count(), 0);
    }

    #[test]
    fn test_debug_format() {
        let mut registry = PluginRegistry::new();
        registry.register_processor(Box::new(MockProcessor));
        let debug_str = format!("{:?}", registry);
        assert!(debug_str.contains("PluginRegistry"));
    }
}