indieweb 0.10.0

A collection of utilities for working with the IndieWeb.
Documentation
//! Known Micropub properties for efficient lookup.
//!
//! This module provides a HashSet-based lookup for known Micropub properties,
//! enabling O(1) property identification instead of O(n) linear searches.

use std::sync::OnceLock;
use std::collections::HashSet;

/// Static set of all known Micropub extension properties.
///
/// This includes both the standard Micropub properties and commonly-used extensions.
/// Using a HashSet allows for O(1) lookup performance when filtering properties.
static KNOWN_PROPERTIES: OnceLock<HashSet<&'static str>> = OnceLock::new();

/// Returns a reference to the set of known Micropub properties.
///
/// Properties are initialized once and reused across all lookups.
/// This includes:
/// - Core Micropub parameters (slug, destination, etc.)
/// - Post status extension
/// - Visibility extension  
/// - Syndication extension
/// - Channel extension
/// - Audience extension
/// - Category property
pub fn get_known_properties() -> &'static HashSet<&'static str> {
    KNOWN_PROPERTIES.get_or_init(|| {
        let mut set = HashSet::new();
        
        // Core Micropub extensions
        set.insert("post-status");
        set.insert("status");
        set.insert("mp-post-status");
        
        set.insert("category");
        set.insert("mp-category");
        
        set.insert("slug");
        set.insert("mp-slug");
        
        set.insert("destination");
        set.insert("mp-destination");
        
        set.insert("audience");
        set.insert("mp-audience");
        
        set.insert("visibility");
        set.insert("mp-visibility");
        
        // Syndication extension
        set.insert("syndicate-to");
        set.insert("mp-syndicate-to");
        
        // Channel extension
        set.insert("channel");
        set.insert("mp-channel");
        
        set
    })
}

/// Checks if a property name is a known Micropub extension property.
///
/// This function performs an O(1) lookup in the known properties HashSet.
///
/// # Arguments
///
/// * `property` - The property name to check (e.g., "post-status", "slug")
///
/// # Returns
///
/// `true` if the property is a known Micropub extension, `false` otherwise.
///
/// # Examples
///
/// ```
/// use indieweb::standards::micropub::known_properties::is_known_property;
///
/// assert!(is_known_property("post-status"));
/// assert!(is_known_property("mp-slug"));
/// assert!(!is_known_property("custom-field"));
/// ```
pub fn is_known_property(property: &str) -> bool {
    let known = get_known_properties();
    known.contains(property) || known.contains(&format!("mp-{}", property).as_str())
}

/// Checks if a property name (with potential "mp-" prefix) is a known Micropub extension.
///
/// This variant is optimized for when you already have the potential prefixed version.
///
/// # Arguments
///
/// * `property` - The property name to check
///
/// # Returns
///
/// `true` if the property or its "mp-" prefixed version is known.
pub fn is_known_property_with_prefix(property: &str) -> bool {
    get_known_properties().contains(property)
}

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

    #[test]
    fn test_known_properties_lookup() {
        // Test core properties
        assert!(is_known_property("post-status"));
        assert!(is_known_property("status"));
        assert!(is_known_property("category"));
        assert!(is_known_property("slug"));
        assert!(is_known_property("destination"));
        assert!(is_known_property("audience"));
        assert!(is_known_property("visibility"));
        assert!(is_known_property("syndicate-to"));
        assert!(is_known_property("channel"));
    }

    #[test]
    fn test_mp_prefix_properties() {
        // Test mp- prefixed versions
        assert!(is_known_property("mp-post-status"));
        assert!(is_known_property("mp-slug"));
        assert!(is_known_property("mp-destination"));
        assert!(is_known_property("mp-audience"));
        assert!(is_known_property("mp-visibility"));
        assert!(is_known_property("mp-syndicate-to"));
        assert!(is_known_property("mp-channel"));
    }

    #[test]
    fn test_unknown_properties() {
        // Test that unknown properties return false
        assert!(!is_known_property("custom-field"));
        assert!(!is_known_property("random-property"));
        assert!(!is_known_property("content"));
        assert!(!is_known_property("name"));
    }

    #[test]
    fn test_prefix_lookup_optimization() {
        // Direct lookup should work
        assert!(is_known_property_with_prefix("post-status"));
        assert!(is_known_property_with_prefix("mp-slug"));
        
        // Should not find unprefixed when checking for prefix
        assert!(!is_known_property_with_prefix("custom"));
    }

    #[test]
    fn test_known_properties_set_initialization() {
        // Ensure the set is properly initialized
        let props = get_known_properties();
        assert!(!props.is_empty());
        assert!(props.len() > 10); // We have many known properties
    }
}