Skip to main content

tono_core/
ids.rs

1//! Stable identifier newtypes for the compiled program (ADR 0003).
2//!
3//! Each wraps a `u64` and exists so a track, pattern, placement, parameter,
4//! or bus can be referenced by a cheap, copyable handle instead of a string.
5//! The IDs are assigned deterministically at compile time in declaration
6//! order, so recompiling an unchanged song reproduces IDENTICAL ids — that is
7//! their entire purpose. The assignment itself happens in the compiler, not
8//! here; this module is just the typed wrappers.
9
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13/// Identifies one instrument track of a compiled song.
14#[derive(
15    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
16)]
17#[serde(transparent)]
18pub struct TrackId(pub u64);
19
20/// Identifies one reusable pattern of a compiled song.
21#[derive(
22    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
23)]
24#[serde(transparent)]
25pub struct PatternId(pub u64);
26
27/// Identifies one placement (a pattern arranged onto a track) of a compiled
28/// song.
29#[derive(
30    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
31)]
32#[serde(transparent)]
33pub struct PlacementId(pub u64);
34
35/// Identifies one automatable parameter of a compiled song.
36#[derive(
37    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
38)]
39#[serde(transparent)]
40pub struct ParamId(pub u64);
41
42/// Identifies one mix bus of a compiled song.
43#[derive(
44    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
45)]
46#[serde(transparent)]
47pub struct BusId(pub u64);
48
49impl TrackId {
50    /// The raw numeric id.
51    pub fn get(self) -> u64 {
52        self.0
53    }
54}
55
56impl PatternId {
57    /// The raw numeric id.
58    pub fn get(self) -> u64 {
59        self.0
60    }
61}
62
63impl PlacementId {
64    /// The raw numeric id.
65    pub fn get(self) -> u64 {
66        self.0
67    }
68}
69
70impl ParamId {
71    /// The raw numeric id.
72    pub fn get(self) -> u64 {
73        self.0
74    }
75}
76
77impl BusId {
78    /// The raw numeric id.
79    pub fn get(self) -> u64 {
80        self.0
81    }
82}
83
84impl From<u64> for TrackId {
85    fn from(n: u64) -> Self {
86        TrackId(n)
87    }
88}
89
90impl From<u64> for PatternId {
91    fn from(n: u64) -> Self {
92        PatternId(n)
93    }
94}
95
96impl From<u64> for PlacementId {
97    fn from(n: u64) -> Self {
98        PlacementId(n)
99    }
100}
101
102impl From<u64> for ParamId {
103    fn from(n: u64) -> Self {
104        ParamId(n)
105    }
106}
107
108impl From<u64> for BusId {
109    fn from(n: u64) -> Self {
110        BusId(n)
111    }
112}
113
114impl std::fmt::Display for TrackId {
115    /// The bare number (`3`), so an id reads the same in logs as in JSON.
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", self.0)
118    }
119}
120
121impl std::fmt::Display for PatternId {
122    /// The bare number (`3`), so an id reads the same in logs as in JSON.
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(f, "{}", self.0)
125    }
126}
127
128impl std::fmt::Display for PlacementId {
129    /// The bare number (`3`), so an id reads the same in logs as in JSON.
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        write!(f, "{}", self.0)
132    }
133}
134
135impl std::fmt::Display for ParamId {
136    /// The bare number (`3`), so an id reads the same in logs as in JSON.
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        write!(f, "{}", self.0)
139    }
140}
141
142impl std::fmt::Display for BusId {
143    /// The bare number (`3`), so an id reads the same in logs as in JSON.
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{}", self.0)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn serde_round_trip_is_the_bare_number() {
155        assert_eq!(serde_json::to_string(&TrackId::from(3)).unwrap(), "3");
156        assert_eq!(serde_json::to_string(&BusId::from(9)).unwrap(), "9");
157        let id: TrackId = serde_json::from_str("3").unwrap();
158        assert_eq!(id, TrackId(3));
159        assert_eq!(id.get(), 3);
160    }
161
162    #[test]
163    fn display_is_the_bare_number() {
164        assert_eq!(TrackId(3).to_string(), "3");
165        assert_eq!(PlacementId(17).to_string(), "17");
166    }
167}