lingxia-lxapp 0.16.0

LxApp (lightweight application) container and runtime for LingXia framework
Documentation
use serde::{Deserialize, Serialize};

pub const ALLOWED_HOST_KINDS: &[&str] = &["root", "view", "text", "tappable", "video"];

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RootRef {
    pub surface_instance_id: String,
    pub page_instance_id: String,
    pub document_instance_id: String,
    pub root_key: String,
    pub root_epoch: u64,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NodeRef {
    pub surface_instance_id: String,
    pub page_instance_id: String,
    pub document_instance_id: String,
    pub root_key: String,
    pub root_epoch: u64,
    pub node_key: String,
    pub node_epoch: u64,
}

impl NodeRef {
    pub fn root(&self) -> RootRef {
        RootRef {
            surface_instance_id: self.surface_instance_id.clone(),
            page_instance_id: self.page_instance_id.clone(),
            document_instance_id: self.document_instance_id.clone(),
            root_key: self.root_key.clone(),
            root_epoch: self.root_epoch,
        }
    }

    pub fn same_root(&self, root: &RootRef) -> bool {
        self.surface_instance_id == root.surface_instance_id
            && self.page_instance_id == root.page_instance_id
            && self.document_instance_id == root.document_instance_id
            && self.root_key == root.root_key
            && self.root_epoch == root.root_epoch
    }
}

impl RootRef {
    pub fn same_generation(&self, other: &RootRef) -> bool {
        self.surface_instance_id == other.surface_instance_id
            && self.page_instance_id == other.page_instance_id
            && self.document_instance_id == other.document_instance_id
            && self.root_key == other.root_key
            && self.root_epoch == other.root_epoch
    }

    pub fn same_document(&self, other: &RootRef) -> bool {
        self.surface_instance_id == other.surface_instance_id
            && self.page_instance_id == other.page_instance_id
            && self.document_instance_id == other.document_instance_id
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeNode {
    #[serde(rename = "ref")]
    pub node_ref: NodeRef,
    pub kind: String,
    pub parent: Option<NodeRef>,
    pub order: u32,
    pub author_type: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub author_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub automation_id: Option<String>,
    #[serde(default)]
    pub props: serde_json::Value,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "camelCase")]
pub enum NativeRootOperation {
    Mount {
        node: NativeNode,
    },
    Update {
        node: NodeRef,
        patch: serde_json::Value,
    },
    Reparent {
        node: NodeRef,
        parent: Option<NodeRef>,
    },
    Reorder {
        node: NodeRef,
        order: u32,
    },
    Unmount {
        node: NodeRef,
    },
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeRootCommit {
    pub action: String,
    pub root: RootRef,
    pub base_revision: u64,
    pub revision: u64,
    pub operations: Vec<NativeRootOperation>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "action")]
pub enum NativeRootAck {
    #[serde(rename = "root.applied", rename_all = "camelCase")]
    Applied { root: RootRef, revision: u64 },
    #[serde(rename = "root.resyncRequired", rename_all = "camelCase")]
    ResyncRequired {
        root: RootRef,
        last_applied_revision: u64,
    },
    #[serde(rename = "root.quiesced", rename_all = "camelCase")]
    Quiesced {
        root: RootRef,
        lease_id: Option<String>,
        reason: String,
    },
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeError {
    pub code: NativeErrorCode,
    pub scope: ErrorScope,
    pub recoverable: bool,
    pub root: RootRef,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub node: Option<NodeRef>,
    pub message: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ErrorScope {
    Node,
    Root,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NativeErrorCode {
    #[serde(rename = "NATIVE_ROOT_UNAVAILABLE")]
    RootUnavailable,
    #[serde(rename = "NATIVE_ROOT_INCOMPATIBLE")]
    RootIncompatible,
    #[serde(rename = "NATIVE_ROOT_INVALID_STRUCTURE")]
    InvalidStructure,
    #[serde(rename = "NATIVE_ROOT_FAILED")]
    RootFailed,
    #[serde(rename = "NATIVE_ROOT_UNSUPPORTED_LAYOUT")]
    UnsupportedLayout,
    #[serde(rename = "NATIVE_COMPONENT_INVALID_PROPS")]
    InvalidProps,
    #[serde(rename = "NATIVE_COMPONENT_MOUNT_FAILED")]
    MountFailed,
    #[serde(rename = "NATIVE_COMPONENT_COMMAND_FAILED")]
    CommandFailed,
    #[serde(rename = "NATIVE_ROOT_UNSUPPORTED_STYLE")]
    UnsupportedStyle,
    #[serde(rename = "NATIVE_ROOT_DESTROYED")]
    RootDestroyed,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RootLifecycle {
    Negotiating,
    Mounting,
    Ready,
    Unavailable,
    Failed,
    Destroyed,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum GeometryRootStatus {
    Applied,
    PendingTree,
    StaleGeneration,
    StaleRevision,
    IdentityInvalid,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rect {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

impl Rect {
    pub fn intersection(&self, other: &Self) -> Self {
        let x = self.x.max(other.x);
        let y = self.y.max(other.y);
        Self {
            x,
            y,
            width: ((self.x + self.width).min(other.x + other.width) - x).max(0.0),
            height: ((self.y + self.height).min(other.y + other.height) - y).max(0.0),
        }
    }
    /// True when the snapshot has a real layout, not the 0×0 / 1×1 placeholder
    /// used before the first measure (or while the document is hidden).
    pub fn is_measured(&self) -> bool {
        self.width >= 2.0 && self.height >= 2.0
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrollChainAncestor {
    pub key: String,
    pub viewport_rect: Rect,
    pub offset_x: f64,
    pub offset_y: f64,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScrollChain {
    pub chain_key: String,
    pub ancestors: Vec<ScrollChainAncestor>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeGeometrySnapshotRoot {
    #[serde(rename = "ref")]
    pub root_ref: RootRef,
    pub basis_tree_revision: u64,
    pub root_order: u32,
    pub chain_key: String,
    pub content_rect: Rect,
    pub visible: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeGeometrySnapshotNode {
    #[serde(rename = "ref")]
    pub node_ref: NodeRef,
    pub chain_key: String,
    pub content_rect: Rect,
    #[serde(default)]
    pub clip_stack: Vec<serde_json::Value>,
    pub visible: bool,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeGeometrySnapshot {
    pub action: String,
    pub surface_instance_id: String,
    pub page_instance_id: String,
    pub document_instance_id: String,
    pub revision: u64,
    pub coordinate_space: String,
    pub roots: Vec<NativeGeometrySnapshotRoot>,
    pub nodes: Vec<NativeGeometrySnapshotNode>,
    pub chains: Vec<ScrollChain>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeometryResultRoot {
    pub root: RootRef,
    pub basis_tree_revision: u64,
    pub last_applied_tree_revision: u64,
    pub status: GeometryRootStatus,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeGeometryResult {
    pub action: String,
    pub surface_instance_id: String,
    pub page_instance_id: String,
    pub document_instance_id: String,
    pub revision: u64,
    pub roots: Vec<GeometryResultRoot>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "action")]
pub enum NativeRootLeaseMessage {
    #[serde(rename = "root.leaseGranted", rename_all = "camelCase")]
    LeaseGranted {
        root: RootRef,
        lease_id: String,
        sequence: u64,
        lease_duration_ms: u64,
    },
    #[serde(rename = "root.leaseAccept", rename_all = "camelCase")]
    LeaseAccept {
        root: RootRef,
        lease_id: String,
        sequence: u64,
    },
    #[serde(rename = "root.leaseActive", rename_all = "camelCase")]
    LeaseActive {
        root: RootRef,
        lease_id: String,
        sequence: u64,
    },
    #[serde(rename = "root.leaseRenew", rename_all = "camelCase")]
    LeaseRenew {
        root: RootRef,
        lease_id: String,
        sequence: u64,
    },
    #[serde(rename = "root.leaseRenewGranted", rename_all = "camelCase")]
    LeaseRenewGranted {
        root: RootRef,
        lease_id: String,
        sequence: u64,
        lease_duration_ms: u64,
    },
    #[serde(rename = "root.leaseRenewAccept", rename_all = "camelCase")]
    LeaseRenewAccept {
        root: RootRef,
        lease_id: String,
        sequence: u64,
    },
    #[serde(rename = "root.leaseRevoked", rename_all = "camelCase")]
    LeaseRevoked {
        root: RootRef,
        lease_id: String,
        sequence: u64,
        reason: String,
    },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostFactoryKind {
    Root,
    View,
    Text,
    Tappable,
    Video,
}

impl HostFactoryKind {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Root => "root",
            Self::View => "view",
            Self::Text => "text",
            Self::Tappable => "tappable",
            Self::Video => "video",
        }
    }

    pub fn parse(kind: &str) -> Option<Self> {
        match kind {
            "root" => Some(Self::Root),
            "view" => Some(Self::View),
            "text" => Some(Self::Text),
            "tappable" => Some(Self::Tappable),
            "video" => Some(Self::Video),
            _ => None,
        }
    }
}