1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0

use serde::Deserialize;
use serde_xml_rs as xml;
use std::fs::File;
use std::io::{self, BufReader};

const X11_BASE_RULES: &str = "/usr/share/X11/xkb/rules/base.xml";

/// A list of keyboard layouts parsed from `/usr/share/X11/xkb/rules/base.xml`.
#[derive(Debug, Deserialize)]
pub struct KeyboardLayouts {
    #[serde(rename = "layoutList")]
    pub layout_list: LayoutList,
}

impl KeyboardLayouts {
    /// Fetch the layouts from the layout list.
    pub fn layouts(&self) -> &[KeyboardLayout] { &self.layout_list.layout }

    /// Fetch the layouts from the layout list.
    pub fn layouts_mut(&mut self) -> &mut [KeyboardLayout] { &mut self.layout_list.layout }
}

/// A list of keyboard layouts.
#[derive(Debug, Deserialize)]
pub struct LayoutList {
    pub layout: Vec<KeyboardLayout>,
}

/// A keyboard layout, which contains an optional list of variants, a name, and a description.
#[derive(Debug, Deserialize)]
pub struct KeyboardLayout {
    #[serde(rename = "configItem")]
    pub config_item:  ConfigItem,
    #[serde(rename = "variantList")]
    pub variant_list: Option<VariantList>,
}

impl KeyboardLayout {
    /// Fetches the name of the keyboard layout.
    pub fn name(&self) -> &str { &self.config_item.name }

    /// Fetches a description of the layout.
    pub fn description(&self) -> &str { &self.config_item.description }

    /// Fetches a list of possible layout variants.
    pub fn variants(&self) -> Option<&Vec<KeyboardVariant>> {
        self.variant_list.as_ref().and_then(|x| x.variant.as_ref())
    }
}

/// Contains the name and description of a keyboard layout.
#[derive(Debug, Deserialize)]
pub struct ConfigItem {
    pub name:              String,
    #[serde(rename = "shortDescription")]
    pub short_description: Option<String>,
    pub description:       String,
}

/// A list of possible variants of a keyboard layout.
#[derive(Debug, Deserialize)]
pub struct VariantList {
    pub variant: Option<Vec<KeyboardVariant>>,
}

/// A variant of a keyboard layout.
#[derive(Debug, Deserialize)]
pub struct KeyboardVariant {
    #[serde(rename = "configItem")]
    pub config_item: ConfigItem,
}

impl KeyboardVariant {
    /// The name of this variant of a keybaord layout.
    pub fn name(&self) -> &str { &self.config_item.name }

    /// A description of this variant of a keyboard layout.
    pub fn description(&self) -> &str { &self.config_item.description }
}

/// Fetches a list of keyboard layouts from `/usr/share/X11/xkb/rules/base.xml`.
pub fn keyboard_layouts() -> io::Result<KeyboardLayouts> {
    xml::from_reader(BufReader::new(File::open(X11_BASE_RULES)?))
        .map_err(|why| io::Error::new(io::ErrorKind::InvalidData, format!("{}", why)))
}