1use serde::Deserialize;
5use serde_xml_rs as xml;
6use std::fs::File;
7use std::io::{self, BufReader};
8
9const X11_BASE_RULES: &str = "/usr/share/X11/xkb/rules/base.xml";
10const X11_EXTRAS_RULES: &str = "/usr/share/X11/xkb/rules/base.extras.xml";
11
12#[derive(Debug, Deserialize, Clone)]
14pub struct KeyboardLayouts {
15 #[serde(rename = "layoutList")]
16 pub layout_list: LayoutList,
17}
18
19impl KeyboardLayouts {
20 pub fn layouts(&self) -> &[KeyboardLayout] { &self.layout_list.layout }
22
23 pub fn layouts_mut(&mut self) -> &mut [KeyboardLayout] { &mut self.layout_list.layout }
25}
26
27#[derive(Debug, Deserialize, Clone)]
29pub struct LayoutList {
30 pub layout: Vec<KeyboardLayout>,
31}
32
33#[derive(Debug, Deserialize, Clone)]
35pub struct KeyboardLayout {
36 #[serde(rename = "configItem")]
37 pub config_item: ConfigItem,
38 #[serde(rename = "variantList")]
39 pub variant_list: Option<VariantList>,
40}
41
42impl KeyboardLayout {
43 pub fn name(&self) -> &str { &self.config_item.name }
45
46 pub fn description(&self) -> &str { &self.config_item.description }
48
49 pub fn variants(&self) -> Option<&Vec<KeyboardVariant>> {
51 self.variant_list.as_ref().and_then(|x| x.variant.as_ref())
52 }
53}
54
55#[derive(Debug, Deserialize, Clone)]
57pub struct ConfigItem {
58 pub name: String,
59 #[serde(rename = "shortDescription")]
60 pub short_description: Option<String>,
61 pub description: String,
62}
63
64#[derive(Debug, Deserialize, Clone)]
66pub struct VariantList {
67 pub variant: Option<Vec<KeyboardVariant>>,
68}
69
70#[derive(Debug, Deserialize, Clone)]
72pub struct KeyboardVariant {
73 #[serde(rename = "configItem")]
74 pub config_item: ConfigItem,
75}
76
77impl KeyboardVariant {
78 pub fn name(&self) -> &str { &self.config_item.name }
80
81 pub fn description(&self) -> &str { &self.config_item.description }
83}
84
85pub fn get_keyboard_layouts(path: &str) -> io::Result<KeyboardLayouts> {
87 xml::from_reader(BufReader::new(File::open(path)?))
88 .map_err(|why| io::Error::new(io::ErrorKind::InvalidData, format!("{}", why)))
89}
90
91pub fn keyboard_layouts() -> io::Result<KeyboardLayouts> {
93 if let Ok(x11_base_rules_xml) = std::env::var("X11_BASE_RULES_XML") {
94 get_keyboard_layouts(&x11_base_rules_xml)
95 }
96 else {
97 get_keyboard_layouts(X11_BASE_RULES)
98 }
99}
100
101pub fn extra_keyboard_layouts() -> io::Result<KeyboardLayouts> {
103 if let Ok(x11_extra_rules_xml) = std::env::var("X11_EXTRA_RULES_XML") {
104 get_keyboard_layouts(&x11_extra_rules_xml)
105 }
106 else {
107 get_keyboard_layouts(X11_EXTRAS_RULES)
108 }
109}
110
111pub fn all_keyboard_layouts() -> io::Result<KeyboardLayouts> {
114 let base_rules = keyboard_layouts();
115 let extras_rules = extra_keyboard_layouts();
116
117 match (base_rules, extras_rules,) {
118 (Ok(base_rules), Ok(extras_rules)) => return Ok(merge_rules(base_rules, extras_rules)),
119 (Err(why), _) => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{}", why))),
120 (_, Err(why)) => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("{}", why))),
121 }
122}
123
124fn merge_rules(base: KeyboardLayouts, extras: KeyboardLayouts) -> KeyboardLayouts {
125 KeyboardLayouts {
126 layout_list: concat_layout_lists(vec![base.layout_list, extras.layout_list])
127 }
128}
129
130fn concat_layout_lists(layouts: Vec<LayoutList>) -> LayoutList {
131 let mut new_layouts = vec![];
132 for layout_list in layouts.into_iter() {
133 new_layouts.extend(layout_list.layout);
134 }
135 return LayoutList { layout: new_layouts }
136}