rhythm_open_exchange/model/
note.rs1use rkyv::{Archive, Deserialize, Serialize};
4use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
5
6#[derive(
8 Debug,
9 Clone,
10 Copy,
11 PartialEq,
12 Eq,
13 Archive,
14 Serialize,
15 Deserialize,
16 SerdeSerialize,
17 SerdeDeserialize,
18)]
19#[serde(tag = "type", content = "data")]
20pub enum NoteType {
21 Tap,
23 Hold { duration_us: i64 },
25 Burst { duration_us: i64 },
27 Mine,
29}
30
31#[derive(
33 Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize, SerdeSerialize, SerdeDeserialize,
34)]
35pub struct Note {
36 pub time_us: i64,
38 pub note_type: NoteType,
40 pub hitsound_index: Option<u16>,
42 pub column: u8,
44}
45
46impl Note {
47 #[must_use]
49 pub fn tap(time_us: i64, column: u8) -> Self {
50 Self {
51 time_us,
52 column,
53 note_type: NoteType::Tap,
54 hitsound_index: None,
55 }
56 }
57
58 #[must_use]
60 pub fn hold(time_us: i64, duration_us: i64, column: u8) -> Self {
61 Self {
62 time_us,
63 column,
64 note_type: NoteType::Hold { duration_us },
65 hitsound_index: None,
66 }
67 }
68
69 #[must_use]
71 pub fn burst(time_us: i64, duration_us: i64, column: u8) -> Self {
72 Self {
73 time_us,
74 column,
75 note_type: NoteType::Burst { duration_us },
76 hitsound_index: None,
77 }
78 }
79
80 #[must_use]
82 pub fn mine(time_us: i64, column: u8) -> Self {
83 Self {
84 time_us,
85 column,
86 note_type: NoteType::Mine,
87 hitsound_index: None,
88 }
89 }
90
91 #[must_use]
93 pub fn is_hold(&self) -> bool {
94 matches!(self.note_type, NoteType::Hold { .. })
95 }
96
97 #[must_use]
99 pub fn is_burst(&self) -> bool {
100 matches!(self.note_type, NoteType::Burst { .. })
101 }
102
103 #[must_use]
105 pub fn is_mine(&self) -> bool {
106 matches!(self.note_type, NoteType::Mine)
107 }
108
109 #[must_use]
111 pub fn duration_us(&self) -> i64 {
112 match self.note_type {
113 NoteType::Tap | NoteType::Mine => 0,
114 NoteType::Hold { duration_us } | NoteType::Burst { duration_us } => duration_us,
115 }
116 }
117
118 #[must_use]
120 pub fn end_time_us(&self) -> i64 {
121 self.time_us + self.duration_us()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_note_tap_constructor() {
131 let note = Note::tap(1_000_000, 2);
132
133 assert_eq!(note.time_us, 1_000_000);
134 assert_eq!(note.column, 2);
135 assert!(matches!(note.note_type, NoteType::Tap));
136 assert!(note.hitsound_index.is_none());
137 }
138
139 #[test]
140 fn test_note_hold_constructor() {
141 let note = Note::hold(2_000_000, 500_000, 1);
142
143 assert_eq!(note.time_us, 2_000_000);
144 assert_eq!(note.column, 1);
145 assert!(matches!(
146 note.note_type,
147 NoteType::Hold {
148 duration_us: 500_000
149 }
150 ));
151 }
152
153 #[test]
154 fn test_note_burst_constructor() {
155 let note = Note::burst(3_000_000, 300_000, 3);
156
157 assert_eq!(note.time_us, 3_000_000);
158 assert_eq!(note.column, 3);
159 assert!(matches!(
160 note.note_type,
161 NoteType::Burst {
162 duration_us: 300_000
163 }
164 ));
165 }
166
167 #[test]
168 fn test_note_mine_constructor() {
169 let note = Note::mine(4_000_000, 0);
170
171 assert_eq!(note.time_us, 4_000_000);
172 assert_eq!(note.column, 0);
173 assert!(matches!(note.note_type, NoteType::Mine));
174 }
175
176 #[test]
177 fn test_note_is_hold() {
178 assert!(!Note::tap(0, 0).is_hold());
179 assert!(Note::hold(0, 100, 0).is_hold());
180 assert!(!Note::burst(0, 100, 0).is_hold());
181 assert!(!Note::mine(0, 0).is_hold());
182 }
183
184 #[test]
185 fn test_note_is_burst() {
186 assert!(!Note::tap(0, 0).is_burst());
187 assert!(!Note::hold(0, 100, 0).is_burst());
188 assert!(Note::burst(0, 100, 0).is_burst());
189 assert!(!Note::mine(0, 0).is_burst());
190 }
191
192 #[test]
193 fn test_note_is_mine() {
194 assert!(!Note::tap(0, 0).is_mine());
195 assert!(!Note::hold(0, 100, 0).is_mine());
196 assert!(!Note::burst(0, 100, 0).is_mine());
197 assert!(Note::mine(0, 0).is_mine());
198 }
199
200 #[test]
201 fn test_note_duration_us() {
202 assert_eq!(Note::tap(0, 0).duration_us(), 0);
203 assert_eq!(Note::hold(0, 500_000, 0).duration_us(), 500_000);
204 assert_eq!(Note::burst(0, 300_000, 0).duration_us(), 300_000);
205 assert_eq!(Note::mine(0, 0).duration_us(), 0);
206 }
207
208 #[test]
209 fn test_note_end_time_us() {
210 assert_eq!(Note::tap(1_000_000, 0).end_time_us(), 1_000_000);
211 assert_eq!(Note::hold(1_000_000, 500_000, 0).end_time_us(), 1_500_000);
212 assert_eq!(Note::burst(2_000_000, 300_000, 0).end_time_us(), 2_300_000);
213 assert_eq!(Note::mine(3_000_000, 0).end_time_us(), 3_000_000);
214 }
215}