Skip to main content

libdof/
dofinitions.rs

1//!Contains most types to represent elements of a keyboard layout with
2
3use std::{convert::Infallible, fmt::Display, str::FromStr};
4
5use crate::{Anchor, DofError, DofErrorInner, Fingering, Keyboard, Result};
6
7/// Represents a finger. Implements `ToString` and `FromStr`, where each finger can either be represented
8/// in string form as `LP`, `LR` (left pinky, left ring) or as a number where `LP`= 0, `LR`= 1 up to
9/// `RP`= 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
11pub enum Finger {
12    /// Left Pinky
13    LP,
14    /// Left Ring
15    LR,
16    /// Left Middle
17    LM,
18    /// Left Index
19    LI,
20    /// Left Thumb
21    LT,
22    /// Right Thumb
23    RT,
24    /// Right Index
25    RI,
26    /// Right Middle
27    RM,
28    /// Right Ring
29    RR,
30    /// Right Pinky
31    RP,
32}
33
34/// Enum to specify both hands. Used in combination with [`Finger`](crate::dofinitions::Finger).
35#[derive(Debug, Copy, Clone, PartialEq)]
36pub enum Hand {
37    /// Left hand
38    Left,
39    /// Right hand
40    Right,
41}
42
43impl Finger {
44    /// Array containing all 10 fingers in order from `LP` to `RP`.
45    pub const FINGERS: [Self; 10] = [
46        Self::LP,
47        Self::LR,
48        Self::LM,
49        Self::LI,
50        Self::LT,
51        Self::RT,
52        Self::RI,
53        Self::RM,
54        Self::RR,
55        Self::RP,
56    ];
57
58    /// Checks if the finger is left or right pinky
59    pub const fn is_pinky(&self) -> bool {
60        matches!(self, Self::LP | Self::RP)
61    }
62
63    /// Checks if the finger is left or right ring
64    pub const fn is_ring(&self) -> bool {
65        matches!(self, Self::LR | Self::RR)
66    }
67
68    /// Checks if the finger is left or right middle
69    pub const fn is_middle(&self) -> bool {
70        matches!(self, Self::LM | Self::RM)
71    }
72
73    /// Checks if the finger is left or right index
74    pub const fn is_index(&self) -> bool {
75        matches!(self, Self::LI | Self::RI)
76    }
77
78    /// Checks if the finger is left or right thumb
79    pub const fn is_thumb(&self) -> bool {
80        matches!(self, Self::LT | Self::RT)
81    }
82
83    /// Returns which `Hand` the finger is on.
84    pub const fn hand(&self) -> Hand {
85        use Finger::*;
86
87        match self {
88            LP | LR | LM | LI | LT => Hand::Left,
89            RP | RR | RM | RI | RT => Hand::Right,
90        }
91    }
92
93    /// Checks if the finger is on the left hand (includes thumb)
94    pub const fn is_on_left_hand(&self) -> bool {
95        matches!(self.hand(), Hand::Left)
96    }
97
98    /// Checks if the finger is on the right hand (includes thumb)
99    pub const fn is_on_right_hand(&self) -> bool {
100        matches!(self.hand(), Hand::Right)
101    }
102}
103
104impl Display for Finger {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{self:?}")
107    }
108}
109
110impl FromStr for Finger {
111    type Err = DofError;
112
113    fn from_str(s: &str) -> Result<Self> {
114        use Finger::*;
115
116        let s = s.trim_start().trim_end();
117        match s {
118            "lp" | "LP" | "0" => Ok(LP),
119            "lr" | "LR" | "1" => Ok(LR),
120            "lm" | "LM" | "2" => Ok(LM),
121            "li" | "LI" | "3" => Ok(LI),
122            "lt" | "LT" | "4" => Ok(LT),
123            "rt" | "RT" | "5" => Ok(RT),
124            "ri" | "RI" | "6" => Ok(RI),
125            "rm" | "RM" | "7" => Ok(RM),
126            "rr" | "RR" | "8" => Ok(RR),
127            "rp" | "RP" | "9" => Ok(RP),
128            _ => Err(DofErrorInner::FingerParseError(s.to_string()).into()),
129        }
130    }
131}
132
133/// Represents known fingerings with names. Currently these are `Traditional` and `Angle`. A `Custom` type
134/// is also specified, though this isn't particularly useful in use with the rest of the library. `FromStr`
135/// uses `standard` and `traditional` for `Traditional`, and `angle` for `Angle`.
136#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
137pub enum NamedFingering {
138    /// Traditional fingering. Default value.
139    #[default]
140    Traditional,
141    /// Fingering for angle mod
142    Angle,
143    /// Any custom type of fingering. This is technically valid in a .dof, but not supported to be worked with.
144    Custom(String),
145}
146
147impl Display for NamedFingering {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        let s = match self {
150            Self::Traditional => "traditional",
151            Self::Angle => "angle",
152            Self::Custom(name) => name.as_str(),
153        };
154
155        write!(f, "{s}")
156    }
157}
158
159impl FromStr for NamedFingering {
160    type Err = std::convert::Infallible;
161
162    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
163        let res = match s.to_lowercase().as_str() {
164            "standard" | "traditional" => Self::Traditional,
165            "angle" => Self::Angle,
166            name => Self::Custom(name.into()),
167        };
168
169        Ok(res)
170    }
171}
172
173/// Covers a wide range of keys that don't necessarily output characters, but are still commonly found on a
174/// keyboard. Shift is meant to function the same as a `Key::Layer { layer: "shift" }` key.
175#[allow(missing_docs)]
176#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
177pub enum SpecialKey {
178    Esc,
179    Repeat,
180    Space,
181    Tab,
182    Enter,
183    Shift,
184    Caps,
185    Ctrl,
186    Alt,
187    Meta,
188    Menu,
189    Fn,
190    Backspace,
191    Del,
192}
193
194/// Covers all keys commonly found on a keyboard. Implements `ToString` and `FromStr`, where the latter has
195/// some rules about how it works:
196/// * if the length is 0, output `Key::Empty`,
197/// * if the length is 1, output:
198///     - `Key::Empty` when it's equal to `~`
199///     - `Key::Transparent` when it's equal to `*`
200///     - `Key::Special(SpecialKey::Space)` when it's equal to a space,
201///     - `Key::Special(SpecialKey::Enter)` when it's equal to `\n`,
202///     - `Key::Special(SpecialKey::Tab)` when it's equal to `\t`,
203///     - `Key::Char` otherwise.
204/// * if the length is more than 1, outputs
205///     - `Key::Char('~')` and `Key::Char('*')` if they contain `\\~` and `\\*` respectively,
206///     - `Key::Special` based on their names in the readme. You can also check the `FromStr`
207///       implementation itself,
208///     - `Key::Layer` if it leads with an `@`.
209///     - `Key::Word` with its first character removed if it starts with `#`, `\\#` or`\\@`,
210///     - `Key::Word` otherwise.
211#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
212pub enum Key {
213    #[default]
214    /// Outputs nothing.
215    Empty,
216    /// Outputs whatever is in on the main layer. Equivalent to `Empty` if on main layer.
217    Transparent,
218    /// Outputs a single character.
219    Char(char),
220    /// Outputs multiple characters.
221    Word(String),
222    /// Outputs a [`SpecialKey`](crate::dofinitions::SpecialKey) like shift or control.
223    Special(SpecialKey),
224    /// Redirects to a different layer when held.
225    Layer {
226        /// Layer key's label.
227        label: String,
228    },
229    /// Specifies a magic key.
230    Magic {
231        /// Magic key's label.
232        label: String,
233    },
234}
235
236impl Key {
237    /// Turns lowercase characters into their qwerty shift output, and turns `Special`` keys `Transparent`.
238    pub fn shifted(&self) -> Self {
239        use Key::*;
240
241        match self {
242            Char(c) => match c {
243                '`' => Char('~'),
244                '1' => Char('!'),
245                '2' => Char('@'),
246                '3' => Char('#'),
247                '4' => Char('$'),
248                '5' => Char('%'),
249                '6' => Char('^'),
250                '7' => Char('*'),
251                '9' => Char('('),
252                '0' => Char(')'),
253                '[' => Char('{'),
254                ']' => Char('}'),
255                '<' => Char('>'),
256                '\'' => Char('"'),
257                ',' => Char('<'),
258                '.' => Char('>'),
259                ';' => Char(':'),
260                '/' => Char('?'),
261                '=' => Char('+'),
262                '-' => Char('_'),
263                '\\' => Char('|'),
264                c => {
265                    let mut upper = c.to_uppercase();
266                    if upper.clone().count() == 1 {
267                        Char(upper.next().unwrap())
268                    } else {
269                        Word(upper.to_string())
270                    }
271                }
272            },
273            Special(_) => Transparent,
274            k => k.clone(),
275        }
276    }
277
278    /// Check if the key is of type [`Key::Char`](crate::dofinitions::Key::Char) which outputs
279    /// a single character.
280    pub const fn is_char(&self) -> bool {
281        matches!(self, Key::Char(_))
282    }
283
284    /// Check if the key is of type [`Key::Word`](crate::dofinitions::Key::Word) which outputs a specific
285    /// string.
286    pub const fn is_word(&self) -> bool {
287        matches!(self, Key::Word(_))
288    }
289
290    /// Check if the key is of type [`Key::Empty`](crate::dofinitions::Key::Empty) which doesn't output
291    /// anything.
292    pub const fn is_empty(&self) -> bool {
293        matches!(self, Key::Empty)
294    }
295
296    /// Check if the key is of type [`Key::Transparent`](crate::dofinitions::Key::Char) which outputs
297    /// whatever it is the main layer outputs in that position.
298    pub const fn is_transparent(&self) -> bool {
299        matches!(self, Key::Transparent)
300    }
301
302    /// Check if the key is of type [`Key::Layer`](crate::dofinitions::Key::Layer), which holds the
303    /// name of a particular layer on the layout
304    pub const fn is_layer(&self) -> bool {
305        matches!(self, Key::Layer { label: _ })
306    }
307
308    /// Check if the key is of type [`Key::Magic`](crate::dofinitions::Key::Magic) which holds the
309    /// name of a particular magic key on the layout
310    pub const fn is_magic(&self) -> bool {
311        matches!(self, Key::Magic { label: _ })
312    }
313
314    /// Get the output if the key is of type [`Key::Char`](crate::dofinitions::Key::Char).
315    pub const fn char_output(&self) -> Option<char> {
316        match self {
317            Key::Char(c) => Some(*c),
318            _ => None,
319        }
320    }
321
322    /// Get the output if the key is of type [`Key::Word`](crate::dofinitions::Key::Word).
323    pub fn word_output(&self) -> Option<&str> {
324        match &self {
325            Key::Word(s) => Some(s),
326            _ => None,
327        }
328    }
329
330    /// Get the layer label if the key is of type [`Key::Layer`](crate::dofinitions::Key::Layer).
331    pub fn layer_label(&self) -> Option<&str> {
332        match &self {
333            Key::Layer { label } => Some(label),
334            _ => None,
335        }
336    }
337
338    /// Get the magic key label if the key is of type [`Key::Magic`](crate::dofinitions::Key::Magic).
339    pub fn magic_label(&self) -> Option<&str> {
340        match &self {
341            Key::Magic { label } => Some(label),
342            _ => None,
343        }
344    }
345}
346
347impl Display for Key {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        use Key::*;
350        use SpecialKey::*;
351
352        let s = match self {
353            Empty => "~".into(),
354            Transparent => "*".into(),
355            Char(c) => match c {
356                n @ ('~' | '*') => format!("\\{n}"),
357                n => String::from(*n),
358            },
359            Word(w) => w.clone(),
360            Special(s) => match s {
361                Esc => "esc".into(),
362                Repeat => "rpt".into(),
363                Space => "spc".into(),
364                Tab => "tab".into(),
365                Enter => "ret".into(),
366                Shift => "sft".into(),
367                Caps => "cps".into(),
368                Ctrl => "ctl".into(),
369                Alt => "alt".into(),
370                Meta => "mt".into(),
371                Menu => "mn".into(),
372                Fn => "fn".into(),
373                Backspace => "bsp".into(),
374                Del => "del".into(),
375            },
376            Layer { label } => format!("@{label}"),
377            Magic { label } => format!("&{label}"),
378        };
379
380        write!(f, "{s}")
381    }
382}
383
384impl FromStr for Key {
385    type Err = Infallible;
386
387    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
388        Ok(s.into())
389    }
390}
391
392impl<T> From<T> for Key
393where
394    T: AsRef<str>,
395{
396    fn from(value: T) -> Self {
397        use Key::*;
398        use SpecialKey::*;
399
400        let s = value.as_ref();
401
402        match s.chars().count() {
403            0 => Empty,
404            1 => match s {
405                "~" => Empty,
406                "*" => Transparent,
407                " " => Special(Space),
408                "\n" => Special(Enter),
409                "\t" => Special(Tab),
410                _ => Char(s.chars().next().unwrap()),
411            },
412            _ => match s.to_lowercase().as_str() {
413                "\\~" => Char('~'),
414                "\\*" => Char('*'),
415                "esc" => Special(Esc),
416                "repeat" | "rpt" => Special(Repeat),
417                "space" | "spc" => Special(Space),
418                "tab" | "tb" => Special(Tab),
419                "enter" | "return" | "ret" | "ent" | "rt" => Special(Enter),
420                "shift" | "shft" | "sft" | "st" => Special(Shift),
421                "caps" | "cps" | "cp" => Special(Caps),
422                "ctrl" | "ctl" | "ct" => Special(Ctrl),
423                "alt" | "lalt" | "ralt" | "lt" => Special(Alt),
424                "meta" | "mta" | "met" | "mt" | "super" | "sup" | "sp" => Special(Meta),
425                "menu" | "mnu" | "mn" => Special(Menu),
426                "fn" => Special(Fn),
427                "backspace" | "bksp" | "bcsp" | "bsp" => Special(Backspace),
428                "del" => Special(Del),
429                _ if s.starts_with('@') => Layer {
430                    label: s.chars().skip(1).collect(),
431                },
432                _ if s.starts_with('&') => Magic {
433                    label: s.chars().skip(1).collect(),
434                },
435                _ if s.starts_with('#')
436                    || s.starts_with("\\#")
437                    || s.starts_with("\\@")
438                    || s.starts_with("\\&") =>
439                {
440                    Word(s.chars().skip(1).collect())
441                }
442                _ => Word(s.into()),
443            },
444        }
445    }
446}
447
448/// Abstraction of `Vec<usize>` where each index represents a row on a layout with a specific amount of keys.
449#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
450pub struct Shape(Vec<usize>);
451
452impl From<Vec<usize>> for Shape {
453    fn from(value: Vec<usize>) -> Self {
454        Shape(value)
455    }
456}
457
458impl<const N: usize> From<[usize; N]> for Shape {
459    fn from(value: [usize; N]) -> Self {
460        Shape(value.into())
461    }
462}
463
464impl Shape {
465    /// Get a slice of all rows in the shape.
466    pub fn inner(&self) -> &[usize] {
467        &self.0
468    }
469
470    /// Consume self to get the Vector of rows in the shape.
471    pub fn into_inner(self) -> Vec<usize> {
472        self.0
473    }
474
475    /// Return the amount of rows in the shape.
476    pub fn row_count(&self) -> usize {
477        self.0.len()
478    }
479
480    /// Get an iterator over each row of the keyboard.
481    fn rows(&self) -> impl Iterator<Item = &usize> {
482        self.inner().iter()
483    }
484
485    /// For each row, checks whether or not it's smaller or equal to the given shape's row.
486    pub fn fits_in(&self, cmp: &Self) -> bool {
487        if self.row_count() > cmp.row_count() {
488            false
489        } else {
490            self.rows().zip(cmp.rows()).all(|(s, c)| s <= c)
491        }
492    }
493}
494
495/// Some default form factors. Options are Ansi, Iso, Ortho (being 3x10 + 3 thumb keys per thumb), Colstag
496/// (being 3x10 + 3 thumb keys per thumb) and a custom option if any anything but the prior options is provided.
497#[allow(missing_docs)]
498#[derive(Debug, Clone, PartialEq, Eq, Hash)]
499pub enum FormFactor {
500    Ansi,
501    Iso,
502    Ortho,
503    Colstag,
504    Custom(String),
505}
506
507impl FormFactor {
508    /// Get the shape of a certain keyboard type.
509    pub fn shape(&self) -> Shape {
510        self.fingering(&NamedFingering::Traditional)
511            .unwrap()
512            .shape()
513    }
514
515    /// Given a known fingering from `NamedFingering`, provide a `Fingering` object with all keys on a board
516    /// like that specified. Will Return an error if any combination is provided that isn't valid, like
517    /// `KeyboardType::Ortho` and `NamedFingering::Angle`.
518    pub fn fingering(&self, named_fingering: &NamedFingering) -> Result<Fingering> {
519        use Finger::*;
520        use FormFactor::*;
521        use NamedFingering::*;
522
523        let fingering = match (self, &named_fingering) {
524            (Ansi, Traditional) => vec![
525                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
526                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
527                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP],
528                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP],
529                vec![LP, LP, LT, LT, RT, RT, RP, RP],
530            ]
531            .into(),
532            (Ansi, Angle) => vec![
533                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
534                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
535                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP],
536                vec![LP, LR, LM, LI, LI, LI, RI, RI, RM, RR, RP, RP],
537                vec![LP, LP, LT, LT, RT, RT, RP, RP],
538            ]
539            .into(),
540            (Iso, Traditional) => vec![
541                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
542                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
543                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP],
544                vec![LP, LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP],
545                vec![LP, LP, LT, LT, RT, RT, RP, RP],
546            ]
547            .into(),
548            (Iso, Angle) => vec![
549                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
550                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP, RP],
551                vec![LP, LP, LR, LM, LI, LI, RI, RI, RM, RR, RP, RP, RP],
552                vec![LP, LP, LR, LM, LI, LI, LI, RI, RI, RM, RR, RP, RP],
553                vec![LP, LP, LT, LT, RT, RT, RP, RP],
554            ]
555            .into(),
556            (Ortho, Traditional) => vec![
557                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
558                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
559                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
560                vec![LT, LT, LT, RT, RT, RT],
561            ]
562            .into(),
563            (Colstag, Traditional) => vec![
564                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
565                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
566                vec![LP, LR, LM, LI, LI, RI, RI, RM, RR, RP],
567                vec![LT, LT, LT, RT, RT, RT],
568            ]
569            .into(),
570            (board, &f) => {
571                return Err(DofErrorInner::UnsupportedKeyboardFingeringCombo(
572                    board.clone(),
573                    f.clone(),
574                )
575                .into());
576            }
577        };
578
579        Ok(fingering)
580    }
581
582    /// Checks if the keyboard is Custom.
583    pub const fn is_custom(&self) -> bool {
584        matches!(self, Self::Custom(_))
585    }
586
587    /// Get the default anchor for each keyboard type. This is (1, 1) for `Ansi` and `Iso` boards (as the
588    /// vast majority of keyboard layouts doesn't remap the number row or special keys on the left) and
589    /// (0, 0) for the rest.
590    pub const fn anchor(&self) -> Anchor {
591        use FormFactor::*;
592
593        match self {
594            Ansi => Anchor::new(1, 1),
595            Iso => Anchor::new(1, 1),
596            Ortho => Anchor::new(0, 0),
597            Colstag => Anchor::new(0, 0),
598            Custom(_) => Anchor::new(0, 0),
599        }
600    }
601}
602
603impl Display for FormFactor {
604    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
605        use FormFactor::*;
606
607        let s = match self {
608            Ansi => "ansi",
609            Iso => "iso",
610            Ortho => "ortho",
611            Colstag => "colstag",
612            Custom(name) => name.as_str(),
613        };
614
615        write!(f, "{s}")
616    }
617}
618
619impl FromStr for FormFactor {
620    type Err = std::convert::Infallible;
621
622    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
623        use FormFactor::*;
624
625        match s.to_lowercase().as_str() {
626            "ansi" => Ok(Ansi),
627            "iso" => Ok(Iso),
628            "ortho" => Ok(Ortho),
629            "colstag" => Ok(Colstag),
630            name => Ok(Custom(name.into())),
631        }
632    }
633}