altium-format 0.1.7

Core altium-cli library for reading and writing Altium Designer files.
Documentation
//! PCB class definitions for organizing objects.
//!
//! Classes group related objects (components, nets, pads, etc.) for
//! design rules, visibility control, and organization.

use crate::types::ParameterCollection;

/// The kind/type of class.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum ClassKind {
    /// Net class - groups nets for routing rules.
    #[default]
    Net = 0,
    /// Component class - groups components.
    Component = 1,
    /// From-To class - groups from-to pairs.
    FromTo = 2,
    /// Pad class - groups pads.
    Pad = 3,
    /// Layer class - groups layers.
    Layer = 4,
    /// Differential pair class.
    DifferentialPair = 5,
    /// Design channel class.
    DesignChannel = 6,
    /// Polygon class - groups polygons.
    Polygon = 7,
    /// Structure class.
    Structure = 8,
    /// Unknown class kind.
    Unknown(u8),
}

impl ClassKind {
    /// Create from integer value.
    pub fn from_int(value: i32) -> Self {
        match value {
            0 => ClassKind::Net,
            1 => ClassKind::Component,
            2 => ClassKind::FromTo,
            3 => ClassKind::Pad,
            4 => ClassKind::Layer,
            5 => ClassKind::DifferentialPair,
            6 => ClassKind::DesignChannel,
            7 => ClassKind::Polygon,
            8 => ClassKind::Structure,
            v => ClassKind::Unknown(v as u8),
        }
    }

    /// Convert to integer value.
    pub fn to_int(self) -> i32 {
        match self {
            ClassKind::Net => 0,
            ClassKind::Component => 1,
            ClassKind::FromTo => 2,
            ClassKind::Pad => 3,
            ClassKind::Layer => 4,
            ClassKind::DifferentialPair => 5,
            ClassKind::DesignChannel => 6,
            ClassKind::Polygon => 7,
            ClassKind::Structure => 8,
            ClassKind::Unknown(v) => v as i32,
        }
    }
}

/// A PCB class definition.
///
/// Classes are used to group related objects (nets, components, pads, etc.)
/// for applying design rules, visibility settings, or organization.
#[derive(Debug, Clone, Default)]
pub struct PcbClass {
    /// Class name.
    pub name: String,
    /// The kind of class (Net, Component, Pad, etc.).
    pub kind: ClassKind,
    /// Whether this is a superclass (contains all objects of this kind).
    pub superclass: bool,
    /// Whether this class was auto-generated.
    pub auto_generated: bool,
    /// Kind of auto-generation (0=none, 1=bottom side, 2=top side).
    pub auto_generated_kind: i32,
    /// Whether this is a schematic auto-generated cluster.
    pub sch_auto_generated_cluster: bool,
    /// Unique ID for the class.
    pub unique_id: String,
    /// Whether the class is selected.
    pub selected: bool,
    /// All parameters (for round-tripping unknown fields).
    pub params: ParameterCollection,
}

impl PcbClass {
    /// Create a new class with the given name and kind.
    pub fn new(name: &str, kind: ClassKind) -> Self {
        Self {
            name: name.to_string(),
            kind,
            ..Default::default()
        }
    }

    /// Parse a class from parameters.
    pub fn from_params(params: &ParameterCollection) -> Self {
        Self {
            name: params
                .get("NAME")
                .map(|v| v.as_str().to_string())
                .unwrap_or_default(),
            kind: params
                .get("KIND")
                .map(|v| ClassKind::from_int(v.as_int_or(0)))
                .unwrap_or_default(),
            superclass: params
                .get("SUPERCLASS")
                .map(|v| v.as_bool_or(false))
                .unwrap_or(false),
            auto_generated: params
                .get("AUTOGENERATEDCLASS")
                .map(|v| v.as_bool_or(false))
                .unwrap_or(false),
            auto_generated_kind: params
                .get("AUTOGENERATEDCLASSKIND")
                .map(|v| v.as_int_or(0))
                .unwrap_or(0),
            sch_auto_generated_cluster: params
                .get("SCHAUTOGENERATEDCLUSTER")
                .map(|v| v.as_bool_or(false))
                .unwrap_or(false),
            unique_id: params
                .get("UNIQUEID")
                .map(|v| v.as_str().to_string())
                .unwrap_or_default(),
            selected: params
                .get("SELECTED")
                .map(|v| v.as_bool_or(false))
                .unwrap_or(false),
            params: params.clone(),
        }
    }

    /// Export to parameters.
    pub fn to_params(&self) -> ParameterCollection {
        let mut params = self.params.clone();
        params.add("NAME", &self.name);
        params.add_int("KIND", self.kind.to_int());
        params.add("SUPERCLASS", if self.superclass { "TRUE" } else { "FALSE" });
        params.add(
            "AUTOGENERATEDCLASS",
            if self.auto_generated { "TRUE" } else { "FALSE" },
        );
        if self.auto_generated_kind != 0 {
            params.add_int("AUTOGENERATEDCLASSKIND", self.auto_generated_kind);
        }
        params.add(
            "SCHAUTOGENERATEDCLUSTER",
            if self.sch_auto_generated_cluster {
                "TRUE"
            } else {
                "FALSE"
            },
        );
        if !self.unique_id.is_empty() {
            params.add("UNIQUEID", &self.unique_id);
        }
        params.add("SELECTED", if self.selected { "TRUE" } else { "FALSE" });
        params
    }
}