microcad-lang 0.5.0

µcad language
Documentation
// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Model

use microcad_lang_base::{Identifier, Refer};
use rustc_hash::FxHashMap;

use crate::{model::*, render::*};

/// The actual model contents
#[derive(custom_debug::Debug, Default)]
pub struct ModelInner {
    /// Optional id.
    ///
    /// The id is set when the model was created by an assignment: `a = Cube(50mm)`.
    pub id: Option<Identifier>,
    /// Parent object.
    #[debug(skip)]
    pub parent: Option<Model>,
    /// Children of the model.
    pub children: Models,
    /// All models that have been created by an assignment.
    pub assignments: FxHashMap<Identifier, Model>,
    /// Element of the model with [SrcRef].
    pub element: Refer<Element>,
    /// Attributes used for export.
    pub attributes: Attributes,
    /// The output type of the this model.
    pub output: Option<RenderOutput>,
}

impl ModelInner {
    /// Create a new [`ModelInner`] with a specific element.
    pub fn new(element: Element, src_ref: SrcRef) -> Self {
        Self {
            element: Refer::new(element, src_ref),
            ..Default::default()
        }
    }

    /// Clone only the content of this model without children and parent.
    pub fn clone_content(&self) -> Self {
        Self {
            id: self.id.clone(),
            parent: None,
            element: self.element.clone(),
            attributes: self.attributes.clone(),
            assignments: self.assignments.clone(),
            output: self.output.clone(),
            ..Default::default()
        }
    }

    /// Return iterator of children.s
    pub fn children(&self) -> std::slice::Iter<'_, Model> {
        self.children.iter()
    }

    /// Return if true model has no children.
    pub fn is_empty(&self) -> bool {
        self.children.is_empty()
    }

    /// Read-only access to the element of this model.
    pub fn element(&self) -> &Element {
        &self.element
    }

    /// Read-only access to the attributes of this model.
    pub fn attributes(&self) -> &Attributes {
        &self.attributes
    }

    /// Returns the render output, panics if there is no render output.
    pub fn output(&self) -> &RenderOutput {
        self.output
            .as_ref()
            .expect("Render output. You have to pre-render() and render() the model first.")
    }

    /// Returns the mutable render output, panics if there is no render output.
    pub fn output_mut(&mut self) -> &mut RenderOutput {
        self.output
            .as_mut()
            .expect("Render output. You have to pre-render() and render() the model first.")
    }
}

impl PropertiesAccess for ModelInner {
    fn get_property(&self, id: &Identifier) -> Option<&Value> {
        self.element.get_property(id)
    }

    fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
        self.element.set_property(id, value)
    }

    fn get_properties(&self) -> Option<&Properties> {
        self.element.get_properties()
    }

    fn add_properties(&mut self, props: Properties) {
        self.element.add_properties(props);
    }
}

impl SrcReferrer for ModelInner {
    fn src_ref(&self) -> SrcRef {
        self.element.src_ref.clone()
    }
}