vize_carton 0.22.0

Carton - The artist's toolbox for Vize compiler
Documentation
//! Optimization flags shared between compiler and runtime.

use bitflags::bitflags;
use serde::{Deserialize, Serialize};

bitflags! {
    /// Patch flags are optimization hints generated by the compiler.
    /// When a block with dynamicChildren is encountered during diff, the algorithm
    /// enters "optimized mode". In this mode, we know that the vdom is produced by
    /// a render function generated by the compiler, so the algorithm only needs to
    /// handle updates explicitly marked by these patch flags.
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
    #[serde(transparent)]
    pub struct PatchFlags: i32 {
        /// Indicates an element with dynamic textContent (children fast path)
        const TEXT = 1;
        /// Indicates an element with dynamic class binding.
        const CLASS = 1 << 1;
        /// Indicates an element with dynamic style
        const STYLE = 1 << 2;
        /// Indicates an element that has non-class/style dynamic props.
        const PROPS = 1 << 3;
        /// Indicates an element with props with dynamic keys.
        const FULL_PROPS = 1 << 4;
        /// Indicates an element that requires props hydration
        const NEED_HYDRATION = 1 << 5;
        /// Indicates a fragment whose children order doesn't change.
        const STABLE_FRAGMENT = 1 << 6;
        /// Indicates a fragment with keyed or partially keyed children
        const KEYED_FRAGMENT = 1 << 7;
        /// Indicates a fragment with unkeyed children.
        const UNKEYED_FRAGMENT = 1 << 8;
        /// Indicates an element that only needs non-props patching
        const NEED_PATCH = 1 << 9;
        /// Indicates a component with dynamic slots
        const DYNAMIC_SLOTS = 1 << 10;
        /// Indicates a fragment created for dev root comments
        const DEV_ROOT_FRAGMENT = 1 << 11;
    }
}

/// Special patch flags (negative values, checked with equality)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(i32)]
pub enum SpecialPatchFlag {
    /// Indicates a cached static vnode
    Cached = -1,
    /// Indicates diffing should bail out of optimized mode
    Bail = -2,
}

impl PatchFlags {
    /// Get the name of a patch flag for dev mode
    pub fn name(&self) -> &'static str {
        match *self {
            Self::TEXT => "TEXT",
            Self::CLASS => "CLASS",
            Self::STYLE => "STYLE",
            Self::PROPS => "PROPS",
            Self::FULL_PROPS => "FULL_PROPS",
            Self::NEED_HYDRATION => "NEED_HYDRATION",
            Self::STABLE_FRAGMENT => "STABLE_FRAGMENT",
            Self::KEYED_FRAGMENT => "KEYED_FRAGMENT",
            Self::UNKEYED_FRAGMENT => "UNKEYED_FRAGMENT",
            Self::NEED_PATCH => "NEED_PATCH",
            Self::DYNAMIC_SLOTS => "DYNAMIC_SLOTS",
            Self::DEV_ROOT_FRAGMENT => "DEV_ROOT_FRAGMENT",
            _ => "UNKNOWN",
        }
    }

    /// Generate a list of flag names for the current value
    pub fn names(&self) -> Vec<&'static str> {
        let mut names = Vec::new();
        if self.contains(Self::TEXT) {
            names.push("TEXT");
        }
        if self.contains(Self::CLASS) {
            names.push("CLASS");
        }
        if self.contains(Self::STYLE) {
            names.push("STYLE");
        }
        if self.contains(Self::PROPS) {
            names.push("PROPS");
        }
        if self.contains(Self::FULL_PROPS) {
            names.push("FULL_PROPS");
        }
        if self.contains(Self::NEED_HYDRATION) {
            names.push("NEED_HYDRATION");
        }
        if self.contains(Self::STABLE_FRAGMENT) {
            names.push("STABLE_FRAGMENT");
        }
        if self.contains(Self::KEYED_FRAGMENT) {
            names.push("KEYED_FRAGMENT");
        }
        if self.contains(Self::UNKEYED_FRAGMENT) {
            names.push("UNKEYED_FRAGMENT");
        }
        if self.contains(Self::NEED_PATCH) {
            names.push("NEED_PATCH");
        }
        if self.contains(Self::DYNAMIC_SLOTS) {
            names.push("DYNAMIC_SLOTS");
        }
        if self.contains(Self::DEV_ROOT_FRAGMENT) {
            names.push("DEV_ROOT_FRAGMENT");
        }
        names
    }
}

bitflags! {
    /// Shape flags for VNode type identification
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
    #[serde(transparent)]
    pub struct ShapeFlags: u32 {
        const ELEMENT = 1;
        const FUNCTIONAL_COMPONENT = 1 << 1;
        const STATEFUL_COMPONENT = 1 << 2;
        const TEXT_CHILDREN = 1 << 3;
        const ARRAY_CHILDREN = 1 << 4;
        const SLOTS_CHILDREN = 1 << 5;
        const TELEPORT = 1 << 6;
        const SUSPENSE = 1 << 7;
        const COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8;
        const COMPONENT_KEPT_ALIVE = 1 << 9;
        /// Combined flag for any component type
        const COMPONENT = Self::STATEFUL_COMPONENT.bits() | Self::FUNCTIONAL_COMPONENT.bits();
    }
}

/// Slot optimization flags
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum SlotFlags {
    /// Stable slots that only reference slot props or context state.
    Stable = 1,
    /// Slots that reference scope variables or has conditional structure.
    Dynamic = 2,
    /// `<slot/>` being forwarded into a child component.
    Forwarded = 3,
}

impl SlotFlags {
    pub fn name(&self) -> &'static str {
        match self {
            Self::Stable => "STABLE",
            Self::Dynamic => "DYNAMIC",
            Self::Forwarded => "FORWARDED",
        }
    }
}

bitflags! {
    /// Flags to optimize vapor `createFor` runtime behavior
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
    #[serde(transparent)]
    pub struct VaporVForFlags: u8 {
        /// v-for is the only child of a parent container
        const FAST_REMOVE = 1;
        /// v-for used on component
        const IS_COMPONENT = 1 << 1;
        /// v-for inside v-once
        const ONCE = 1 << 2;
    }
}

#[cfg(test)]
mod tests {
    use super::{PatchFlags, ShapeFlags, SlotFlags};

    #[test]
    fn test_patch_flags() {
        let flags = PatchFlags::TEXT | PatchFlags::CLASS;
        assert!(flags.contains(PatchFlags::TEXT));
        assert!(flags.contains(PatchFlags::CLASS));
        assert!(!flags.contains(PatchFlags::STYLE));
    }

    #[test]
    fn test_shape_flags_component() {
        assert!(ShapeFlags::COMPONENT.contains(ShapeFlags::STATEFUL_COMPONENT));
        assert!(ShapeFlags::COMPONENT.contains(ShapeFlags::FUNCTIONAL_COMPONENT));
    }

    #[test]
    fn test_slot_flags() {
        assert_eq!(SlotFlags::Stable.name(), "STABLE");
        assert_eq!(SlotFlags::Dynamic.name(), "DYNAMIC");
    }
}