Skip to main content

sim_lib_pitch_serial/
label.rs

1//! Convention-dependent row-form labels.
2
3use std::fmt::{Display, Formatter};
4
5use crate::{RowFamily, RowForm};
6
7/// A printable family/index label such as `P0` or `RI11`.
8///
9/// A label does not replace [`crate::RowOperation`]; it records only the family
10/// and index selected by an explicit [`RowLabelConvention`].
11#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct RowLabel {
13    family: RowFamily,
14    index: u8,
15}
16
17impl RowLabel {
18    /// Constructs a label, reducing the index modulo twelve.
19    pub const fn new(family: RowFamily, index: u8) -> Self {
20        Self {
21            family,
22            index: index % 12,
23        }
24    }
25
26    /// Returns the label family.
27    pub const fn family(self) -> RowFamily {
28        self.family
29    }
30
31    /// Returns the modulo-twelve label index.
32    pub const fn index(self) -> u8 {
33        self.index
34    }
35}
36
37impl Display for RowLabel {
38    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
39        write!(formatter, "{}{}", self.family, self.index)
40    }
41}
42
43/// Policy for projecting an operation-bearing row form to a printed label.
44#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
45pub enum RowLabelConvention {
46    /// Label P/I from the first sounding class and R/RI from the last.
47    ///
48    /// Using the last class for retrogrades keeps a family and its retrograde on
49    /// the same index under the common first/last-pitch convention.
50    FirstLastPitch,
51    /// Label every family with the affine addend of its normalized operation.
52    OperationIndex,
53}
54
55impl RowLabelConvention {
56    /// Returns the stable machine-readable convention name.
57    pub const fn as_str(self) -> &'static str {
58        match self {
59            Self::FirstLastPitch => "first-last-pitch",
60            Self::OperationIndex => "operation-index",
61        }
62    }
63
64    /// Projects `form` to a label without changing its operation identity.
65    pub fn label(self, form: &RowForm) -> RowLabel {
66        let operation = form.operation();
67        let index = match self {
68            Self::FirstLastPitch if operation.family.is_retrograde() => form.classes()[11].value(),
69            Self::FirstLastPitch => form.classes()[0].value(),
70            Self::OperationIndex => operation.addend,
71        };
72        RowLabel::new(operation.family, index)
73    }
74}