spimdisasm 2.0.0-alpha.1

MIPS disassembler
Documentation
/* SPDX-FileCopyrightText: © 2025 Decompollaborate */
/* SPDX-License-Identifier: MIT */

use core::fmt;

use super::{LabelMetadata, LabelType, OwnerSegmentKind};

// Avoid duplicating this function here and in symbol_metadata_name_display
fn should_quote_symbol(name: &str) -> bool {
    name.contains('@')
}

#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd)]
#[must_use]
pub struct LabelMetadataNameDisplay<'label> {
    label: &'label LabelMetadata,
}

impl<'label> LabelMetadataNameDisplay<'label> {
    pub fn new(label: &'label LabelMetadata) -> Self {
        Self { label }
    }
}

impl LabelMetadataNameDisplay<'_> {
    fn display_section_prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.label.is_defined() {
            Ok(())
        } else {
            // Labels don't get a section prefix because most of the time they are in their
            // respective sections.
            // But if it hasn't been defined yet then we haven't seen this label actually defined
            // anywhere, so we tell the user by using this prefix.
            write!(f, "UNK_")
        }
    }

    fn display_type_prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.label.label_type() {
            LabelType::Branch | LabelType::Jumptable => write!(f, ".L"),
            LabelType::GccExceptTable => write!(f, "$LEH_"),
            LabelType::AlternativeEntry => write!(f, "aent_"),
        }
    }

    fn display_suffix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let OwnerSegmentKind::Overlay(name) = self.label.owner_segment_kind() {
            write!(f, "_{name}")?;
        }

        /*
        if GlobalConfig.CUSTOM_SUFFIX:
            suffix += GlobalConfig.CUSTOM_SUFFIX
        */

        Ok(())
    }

    fn display_unique_identifier(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        /*
        if GlobalConfig.SEQUENTIAL_LABEL_NAMES and self.parentFunction is not None:
            if symType in {SymbolSpecialType.branchlabel, SymbolSpecialType.jumptablelabel}:
                index = self.parentFunction.branchLabels.index(self.vram)
                if index is not None:
                    return f"{self.parentFunction.getName()}_{index + 1}"
            elif symType == SymbolSpecialType.jumptable:
                index = self.parentFunction.jumpTables.index(self.vram)
                if index is not None:
                    return f"{self.parentFunction.getName()}_{index + 1}"

        if GlobalConfig.AUTOGENERATED_NAMES_BASED_ON_FILE_NAME:
            if self.parentFileName is not None and self.inFileOffset is not None and symType != SymbolSpecialType.function:
                sectionName = self.sectionType.toStr().replace(".", "_")
                return f"{self.parentFileName}{sectionName}_{self.inFileOffset:06X}"
        */

        write!(f, "{}", self.label.vram())?;

        self.display_suffix(f)
    }

    pub fn autogenerate_name(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.display_section_prefix(f)?;
        self.display_type_prefix(f)?;
        self.display_unique_identifier(f)
    }
}

impl fmt::Display for LabelMetadataNameDisplay<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(user_declared_name) = &self.label.user_declared_name() {
            let should_escape = should_quote_symbol(user_declared_name);

            if should_escape {
                write!(f, "\"")?;
            }
            write!(f, "{user_declared_name}")?;
            if should_escape {
                write!(f, "\"")?;
            }
            Ok(())
        } else {
            self.autogenerate_name(f)
        }
    }
}