1use std::collections::HashMap;
70use std::fmt;
71
72use serde::de::{self, DeserializeSeed, SeqAccess, Visitor};
73use serde::{Deserialize, Serialize};
74
75use crate::{
76 ActionDecl, ActionKind, ActionMap, ActionMapError, Binding, BindingParseError, Device,
77 DoubleTap, GAMEPLAY_CONTEXT, Hold, Pattern, Tap,
78};
79
80pub const DEVICE_LISTS: [Device; 4] = [
83 Device::Keyboard,
84 Device::Pointer,
85 Device::Gamepad,
86 Device::Touch,
87];
88
89#[must_use]
91pub const fn list_name(device: Device) -> &'static str {
92 match device {
93 Device::Keyboard => "keyboard",
94 Device::Pointer => "mouse",
95 Device::Gamepad => "gamepad",
96 Device::Touch => "touch",
97 }
98}
99
100const MS_PER_SECOND: f32 = 1000.0;
103
104impl ActionMap {
105 pub fn from_ron(text: &str) -> Result<Self, BindingAssetError> {
113 let headers: Vec<Header> = ron::from_str(text).map_err(BindingAssetError::parse)?;
117 let mut kinds = HashMap::with_capacity(headers.len());
118 for header in headers {
119 kinds.entry(header.action).or_insert(header.kind);
120 }
121 let mut map = Self::new();
122 let mut refusal = None;
123 let read = ron::Options::default().from_str_seed(
124 text,
125 Records {
126 kinds: &kinds,
127 map: &mut map,
128 refusal: &mut refusal,
129 },
130 );
131 match read {
132 Ok(()) => Ok(map),
133 Err(error) => Err(match refusal {
134 Some(refusal) => BindingAssetError::at(&error, refusal),
135 None => BindingAssetError::parse(error),
136 }),
137 }
138 }
139
140 pub fn to_ron(&self) -> Result<String, AssetWriteError> {
158 let records = self
159 .slots
160 .iter()
161 .map(|slot| self.record(slot))
162 .collect::<Result<Vec<_>, _>>()?;
163 let pretty = ron::ser::PrettyConfig::new()
164 .new_line("\n")
165 .indentor(" ")
166 .depth_limit(2);
169 let mut text = ron::ser::to_string_pretty(&records, pretty)
170 .expect("a binding asset has no serializer path that can fail");
171 text.push('\n');
172 Ok(text)
173 }
174
175 fn record(&self, slot: &crate::ActionSlot) -> Result<ActionFile, AssetWriteError> {
177 let name = &slot.decl.name;
178 if slot.repeat.is_some() {
179 return Err(AssetWriteError::Repeat(name.clone()));
180 }
181 let mut file = ActionFile {
182 action: name.clone(),
183 kind: slot.decl.kind.into(),
184 context: self.contexts[slot.context].clone(),
185 keyboard: Vec::new(),
186 mouse: Vec::new(),
187 gamepad: Vec::new(),
188 touch: Vec::new(),
189 patterns: Vec::new(),
190 pad_chords_outrank: slot.pad_chords_outrank,
191 };
192 for binding in &slot.defaults {
193 file.list_mut(binding.device()).push(binding.to_string());
194 }
195 let read_back = DEVICE_LISTS
196 .iter()
197 .flat_map(|device| file.list(*device))
198 .map(String::as_str);
199 if !read_back.eq(slot.defaults.iter().map(ToString::to_string)) {
200 return Err(AssetWriteError::MixedLists(name.clone()));
201 }
202 let emits = |pattern| {
203 self.emits(name, pattern)
204 .map(str::to_owned)
205 .ok_or_else(|| AssetWriteError::NoEmit {
206 action: name.clone(),
207 pattern,
208 })
209 };
210 let ms = |seconds, pattern| {
211 whole_ms(seconds).ok_or_else(|| AssetWriteError::NotWholeMilliseconds {
212 action: name.clone(),
213 pattern,
214 })
215 };
216 if let Some(tap) = self.tap(name) {
217 let time = ms(tap.time(), Pattern::Tap)?;
218 file.patterns
219 .push(PatternFile::Tap(time, emits(Pattern::Tap)?));
220 }
221 if let Some(hold) = self.hold(name) {
222 let time = ms(hold.time(), Pattern::Hold)?;
223 file.patterns
224 .push(PatternFile::Hold(time, emits(Pattern::Hold)?));
225 }
226 if let Some(double) = self.double_tap(name) {
227 let tap_time = ms(double.tap_time(), Pattern::DoubleTap)?;
228 let window = ms(double.window(), Pattern::DoubleTap)?;
229 let target = emits(Pattern::DoubleTap)?;
230 file.patterns.push(if double.fires_on_release() {
231 PatternFile::DoubleTapOnRelease(tap_time, window, target)
232 } else {
233 PatternFile::DoubleTap(tap_time, window, target)
234 });
235 }
236 Ok(file)
237 }
238}
239
240fn whole_ms(seconds: f32) -> Option<u16> {
243 let ms = (seconds * MS_PER_SECOND).round() as u16;
246 (f32::from(ms) / MS_PER_SECOND == seconds).then_some(ms)
247}
248
249const fn pattern_name(pattern: Pattern) -> &'static str {
251 match pattern {
252 Pattern::Tap => "Tap",
253 Pattern::Hold => "Hold",
254 Pattern::DoubleTap => "DoubleTap",
255 }
256}
257
258#[derive(Serialize, Deserialize)]
265#[serde(rename = "Action", deny_unknown_fields)]
266struct ActionFile {
267 action: String,
268 kind: KindFile,
269 #[serde(default = "gameplay", skip_serializing_if = "is_gameplay")]
270 context: String,
271 #[serde(default, skip_serializing_if = "Vec::is_empty")]
272 keyboard: Vec<String>,
273 #[serde(default, skip_serializing_if = "Vec::is_empty")]
274 mouse: Vec<String>,
275 #[serde(default, skip_serializing_if = "Vec::is_empty")]
276 gamepad: Vec<String>,
277 #[serde(default, skip_serializing_if = "Vec::is_empty")]
278 touch: Vec<String>,
279 #[serde(default, skip_serializing_if = "Vec::is_empty")]
280 patterns: Vec<PatternFile>,
281 #[serde(default, skip_serializing_if = "is_false")]
282 pad_chords_outrank: bool,
283}
284
285impl ActionFile {
286 fn list(&self, device: Device) -> &[String] {
287 match device {
288 Device::Keyboard => &self.keyboard,
289 Device::Pointer => &self.mouse,
290 Device::Gamepad => &self.gamepad,
291 Device::Touch => &self.touch,
292 }
293 }
294
295 fn list_mut(&mut self, device: Device) -> &mut Vec<String> {
296 match device {
297 Device::Keyboard => &mut self.keyboard,
298 Device::Pointer => &mut self.mouse,
299 Device::Gamepad => &mut self.gamepad,
300 Device::Touch => &mut self.touch,
301 }
302 }
303}
304
305fn gameplay() -> String {
306 GAMEPLAY_CONTEXT.to_owned()
307}
308
309fn is_gameplay(context: &str) -> bool {
310 context == GAMEPLAY_CONTEXT
311}
312
313fn is_false(flag: &bool) -> bool {
314 !flag
315}
316
317#[derive(Deserialize)]
320#[serde(rename = "Action")]
321struct Header {
322 action: String,
323 kind: KindFile,
324}
325
326#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
328#[serde(rename = "Kind")]
329enum KindFile {
330 Button,
331 Axis1,
332 Axis2,
333}
334
335impl From<ActionKind> for KindFile {
336 fn from(kind: ActionKind) -> Self {
337 match kind {
338 ActionKind::Button => Self::Button,
339 ActionKind::Axis1 => Self::Axis1,
340 ActionKind::Axis2 => Self::Axis2,
341 }
342 }
343}
344
345impl From<KindFile> for ActionKind {
346 fn from(kind: KindFile) -> Self {
347 match kind {
348 KindFile::Button => Self::Button,
349 KindFile::Axis1 => Self::Axis1,
350 KindFile::Axis2 => Self::Axis2,
351 }
352 }
353}
354
355#[derive(Serialize, Deserialize)]
357#[serde(rename = "Pattern")]
358enum PatternFile {
359 Tap(u16, String),
360 Hold(u16, String),
361 DoubleTap(u16, u16, String),
362 DoubleTapOnRelease(u16, u16, String),
363}
364
365impl PatternFile {
366 const fn pattern(&self) -> Pattern {
367 match self {
368 Self::Tap(..) => Pattern::Tap,
369 Self::Hold(..) => Pattern::Hold,
370 Self::DoubleTap(..) | Self::DoubleTapOnRelease(..) => Pattern::DoubleTap,
371 }
372 }
373
374 fn emits(&self) -> &str {
375 match self {
376 Self::Tap(_, emits)
377 | Self::Hold(_, emits)
378 | Self::DoubleTap(_, _, emits)
379 | Self::DoubleTapOnRelease(_, _, emits) => emits,
380 }
381 }
382
383 fn timed(&self) -> Option<Timed> {
386 let seconds = |ms: u16| f32::from(ms) / MS_PER_SECOND;
387 Some(match *self {
388 Self::Tap(ms, _) => Timed::Tap(Tap::new(seconds(ms))?),
389 Self::Hold(ms, _) => Timed::Hold(Hold::new(seconds(ms))?),
390 Self::DoubleTap(tap, window, _) => {
391 Timed::DoubleTap(DoubleTap::new(seconds(tap), seconds(window))?)
392 }
393 Self::DoubleTapOnRelease(tap, window, _) => {
394 Timed::DoubleTap(DoubleTap::new(seconds(tap), seconds(window))?.on_release())
395 }
396 })
397 }
398}
399
400enum Timed {
403 Tap(Tap),
404 Hold(Hold),
405 DoubleTap(DoubleTap),
406}
407
408impl Timed {
409 fn attach(self, map: &mut ActionMap, action: &str, emits: &str) -> Result<(), ActionMapError> {
411 let pattern = match self {
412 Self::Tap(tap) => {
413 map.set_tap(action, Some(tap))?;
414 Pattern::Tap
415 }
416 Self::Hold(hold) => {
417 map.set_hold(action, Some(hold))?;
418 Pattern::Hold
419 }
420 Self::DoubleTap(double) => {
421 map.set_double_tap(action, Some(double))?;
422 Pattern::DoubleTap
423 }
424 };
425 map.set_emits(action, pattern, Some(emits))
426 }
427}
428
429struct Records<'a> {
436 kinds: &'a HashMap<String, KindFile>,
438 map: &'a mut ActionMap,
439 refusal: &'a mut Option<AssetRefusal>,
442}
443
444impl Records<'_> {
445 fn declare(&mut self, record: ActionFile) -> Result<Vec<(Timed, String)>, AssetRefusal> {
448 let action = record.action.clone();
449 let mut bindings = Vec::new();
450 for device in DEVICE_LISTS {
451 for text in record.list(device) {
452 let binding =
453 text.parse::<Binding>()
454 .map_err(|error| AssetRefusal::BadBinding {
455 action: action.clone(),
456 error,
457 })?;
458 if binding.device() != device {
459 return Err(AssetRefusal::WrongList {
460 action,
461 binding: text.clone(),
462 list: device,
463 });
464 }
465 bindings.push(binding);
466 }
467 }
468 let mut seen = [false; Pattern::ALL.len()];
469 let mut timed = Vec::with_capacity(record.patterns.len());
470 for pattern in &record.patterns {
471 let kind = pattern.pattern();
472 let emits = pattern.emits();
473 let Some(times) = pattern.timed() else {
474 return Err(AssetRefusal::BadTime {
475 action,
476 pattern: kind,
477 });
478 };
479 timed.push((times, emits.to_owned()));
480 if std::mem::replace(&mut seen[kind.index()], true) {
481 return Err(AssetRefusal::DuplicatePattern {
482 action,
483 pattern: kind,
484 });
485 }
486 if emits == action {
487 return Err(AssetRefusal::EmitsItself {
488 action,
489 pattern: kind,
490 });
491 }
492 match self.kinds.get(emits) {
493 Some(KindFile::Button) => {}
494 Some(_) => {
495 return Err(AssetRefusal::EmitNotAButton {
496 action,
497 pattern: kind,
498 emits: emits.to_owned(),
499 });
500 }
501 None => {
502 return Err(AssetRefusal::UnknownEmit {
503 action,
504 pattern: kind,
505 emits: emits.to_owned(),
506 });
507 }
508 }
509 }
510 self.map
511 .try_declare_in(
512 &record.context,
513 ActionDecl {
514 name: record.action,
515 kind: record.kind.into(),
516 bindings,
517 },
518 )
519 .and_then(|()| {
520 self.map
521 .set_pad_chords_outrank(&action, record.pad_chords_outrank)
522 })
523 .map_err(AssetRefusal::Declare)?;
524 Ok(timed)
525 }
526
527 fn refuse<E: de::Error>(&mut self, refusal: AssetRefusal) -> E {
529 let error = E::custom(&refusal);
530 *self.refusal = Some(refusal);
531 error
532 }
533}
534
535impl<'de> DeserializeSeed<'de> for Records<'_> {
536 type Value = ();
537
538 fn deserialize<D: de::Deserializer<'de>>(self, deserializer: D) -> Result<(), D::Error> {
539 deserializer.deserialize_seq(self)
540 }
541}
542
543impl<'de> Visitor<'de> for Records<'_> {
544 type Value = ();
545
546 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547 f.write_str("a list of action records")
548 }
549
550 fn visit_seq<A: SeqAccess<'de>>(mut self, mut seq: A) -> Result<(), A::Error> {
551 let mut patterns = Vec::new();
552 while let Some(record) = seq.next_element::<ActionFile>()? {
553 let action = record.action.clone();
554 match self.declare(record) {
555 Ok(timed) => patterns.push((action, timed)),
556 Err(refusal) => return Err(self.refuse(refusal)),
557 }
558 }
559 for (action, timed) in patterns {
560 for (pattern, emits) in timed {
561 if let Err(error) = pattern.attach(self.map, &action, &emits) {
564 return Err(self.refuse(AssetRefusal::Declare(error)));
565 }
566 }
567 }
568 Ok(())
569 }
570}
571
572#[derive(Clone, Debug, PartialEq, Eq)]
579pub struct BindingAssetError {
580 line: usize,
581 column: usize,
582 refusal: AssetRefusal,
583}
584
585impl BindingAssetError {
586 #[must_use]
588 pub const fn line(&self) -> usize {
589 self.line
590 }
591
592 #[must_use]
594 pub const fn column(&self) -> usize {
595 self.column
596 }
597
598 #[must_use]
600 pub const fn refusal(&self) -> &AssetRefusal {
601 &self.refusal
602 }
603
604 fn at(error: &ron::error::SpannedError, refusal: AssetRefusal) -> Self {
605 Self {
606 line: error.span.start.line,
607 column: error.span.start.col,
608 refusal,
609 }
610 }
611
612 fn parse(error: ron::error::SpannedError) -> Self {
613 let message = error.code.to_string();
614 Self::at(&error, AssetRefusal::Parse(message))
615 }
616}
617
618impl fmt::Display for BindingAssetError {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 write!(
621 f,
622 "line {}, column {}: {}",
623 self.line, self.column, self.refusal
624 )
625 }
626}
627
628impl std::error::Error for BindingAssetError {}
629
630#[derive(Clone, Debug, PartialEq, Eq)]
632pub enum AssetRefusal {
633 Parse(String),
636 BadBinding {
638 action: String,
640 error: BindingParseError,
642 },
643 WrongList {
646 action: String,
648 binding: String,
650 list: Device,
652 },
653 DuplicatePattern {
655 action: String,
657 pattern: Pattern,
659 },
660 BadTime {
662 action: String,
664 pattern: Pattern,
666 },
667 UnknownEmit {
669 action: String,
671 pattern: Pattern,
673 emits: String,
675 },
676 EmitNotAButton {
678 action: String,
680 pattern: Pattern,
682 emits: String,
684 },
685 EmitsItself {
687 action: String,
689 pattern: Pattern,
691 },
692 Declare(ActionMapError),
696}
697
698impl fmt::Display for AssetRefusal {
699 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700 match self {
701 Self::Parse(message) => f.write_str(message),
702 Self::BadBinding { action, error } => write!(f, "action {action:?}: {error}"),
703 Self::WrongList {
704 action,
705 binding,
706 list,
707 } => write!(
708 f,
709 "action {action:?}: {binding:?} is not a {} binding",
710 list_name(*list)
711 ),
712 Self::DuplicatePattern { action, pattern } => {
713 let pattern = pattern_name(*pattern);
714 write!(f, "action {action:?}: more than one {pattern}")
715 }
716 Self::BadTime { action, pattern } => {
717 let pattern = pattern_name(*pattern);
718 write!(f, "action {action:?}: a {pattern} of zero milliseconds")
719 }
720 Self::UnknownEmit {
721 action,
722 pattern,
723 emits,
724 } => {
725 let pattern = pattern_name(*pattern);
726 write!(
727 f,
728 "action {action:?}: {pattern} emits {emits:?}, which is not declared"
729 )
730 }
731 Self::EmitNotAButton {
732 action,
733 pattern,
734 emits,
735 } => {
736 let pattern = pattern_name(*pattern);
737 write!(
738 f,
739 "action {action:?}: {pattern} emits {emits:?}, which is not a Button"
740 )
741 }
742 Self::EmitsItself { action, pattern } => {
743 let pattern = pattern_name(*pattern);
744 write!(f, "action {action:?}: {pattern} emits its own action")
745 }
746 Self::Declare(error) => error.fmt(f),
747 }
748 }
749}
750
751#[derive(Clone, Debug, PartialEq, Eq)]
754pub enum AssetWriteError {
755 MixedLists(String),
758 NoEmit {
761 action: String,
763 pattern: Pattern,
765 },
766 NotWholeMilliseconds {
769 action: String,
771 pattern: Pattern,
773 },
774 Repeat(String),
777}
778
779impl fmt::Display for AssetWriteError {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 match self {
782 Self::MixedLists(action) => {
783 write!(f, "action {action:?}: its bindings interleave devices")
784 }
785 Self::NoEmit { action, pattern } => {
786 let pattern = pattern_name(*pattern);
787 write!(f, "action {action:?}: its {pattern} emits no action")
788 }
789 Self::NotWholeMilliseconds { action, pattern } => {
790 let pattern = pattern_name(*pattern);
791 write!(
792 f,
793 "action {action:?}: its {pattern} is not a whole number of milliseconds"
794 )
795 }
796 Self::Repeat(action) => write!(f, "action {action:?}: a repeat has no asset form"),
797 }
798 }
799}
800
801impl std::error::Error for AssetWriteError {}
802
803#[cfg(test)]
804mod tests;