|
1 | | -use std::borrow::Cow; |
| 1 | +use std::{borrow::Cow, path::Path}; |
2 | 2 |
|
3 | | -#[expect(unused)] |
4 | 3 | struct Line<S> { |
5 | 4 | pub hut: S, |
6 | 5 | pub winput: S, |
@@ -73,63 +72,126 @@ fn gen_convert<S1: AsRef<str>, S2: AsRef<str>, I: IntoIterator<Item = (S1, S2)>> |
73 | 72 | format!("// This file is auto-generated. Do not edit manually.\n\n{}", generated_code) |
74 | 73 | } |
75 | 74 |
|
76 | | -fn main() { |
77 | | - let csv_path = "src/keycodes/convert/convert.csv"; |
78 | | - let output_path = "src/keycodes/convert/"; |
79 | 75 |
|
80 | | - let csv_content = std::fs::read_to_string(csv_path).expect("Failed to read convert.csv"); |
81 | | - let csv = csv_content.lines().filter(|i| !i.trim().is_empty()) |
82 | | - .map(|i| i.split(',').collect::<Vec<_>>().into()).collect::<Vec<Line<_>>>(); |
| 76 | +#[expect(unused)] |
| 77 | +#[derive(Debug, Clone, Copy)] |
| 78 | +enum KeyType { |
| 79 | + HUT, Winput, WinVk, VkValue, HutKeyboardValue, Enigo, KeySym, CG, |
| 80 | +} |
83 | 81 |
|
84 | | - fn is_valid<S: AsRef<str>>(s: S) -> bool { |
85 | | - let s = s.as_ref().trim(); |
86 | | - !s.is_empty() && !s.starts_with("n!") && !s.starts_with("na!") && !s.starts_with("todo!") && !s.starts_with("none!") |
| 82 | +impl KeyType { |
| 83 | + pub fn name(self) -> &'static str { |
| 84 | + match self { |
| 85 | + KeyType::HUT => "Usage", |
| 86 | + KeyType::Winput => "Vk", |
| 87 | + KeyType::WinVk => "Vk", |
| 88 | + KeyType::VkValue => unimplemented!(), |
| 89 | + KeyType::HutKeyboardValue => unimplemented!(), |
| 90 | + KeyType::Enigo => "Enigo", |
| 91 | + KeyType::KeySym => "KeySym", |
| 92 | + KeyType::CG => "CGKeyCode", |
| 93 | + } |
87 | 94 | } |
88 | | - fn kv_is_valid<K: AsRef<str>, V: AsRef<str>>((k, v): &(K, V)) -> bool { |
89 | | - is_valid(k) && is_valid(v) && !k.as_ref().trim().ends_with('*') |
| 95 | + |
| 96 | + pub fn get_line<'a>(self, line: &'a Line<&'a str>) -> &'a str { |
| 97 | + match self { |
| 98 | + KeyType::HUT => line.hut, |
| 99 | + KeyType::Winput => line.winput, |
| 100 | + KeyType::WinVk => line.vk, |
| 101 | + KeyType::VkValue => line.vk_value, |
| 102 | + KeyType::HutKeyboardValue => line.hut_keyboard_value, |
| 103 | + KeyType::Enigo => line.enigo, |
| 104 | + KeyType::KeySym => line.keysym, |
| 105 | + KeyType::CG => line.cg, |
| 106 | + } |
90 | 107 | } |
91 | 108 |
|
92 | | - std::fs::write( |
93 | | - format!("{output_path}generated.winput_to_hut.rs"), |
94 | | - gen_convert( |
95 | | - &gen_template(CONVERT_GENERAL, "Vk", "Usage", None, Some(".usage()")), |
96 | | - csv.iter().map(|i| (i.winput, i.hut)).filter(kv_is_valid) |
97 | | - ) |
98 | | - ).expect("Failed to write generated.rs"); |
| 109 | + pub fn as_value_prefix(self) -> Option<&'static str> { |
| 110 | + match self { |
| 111 | + KeyType::WinVk => Some("keys::"), |
| 112 | + _ => None, |
| 113 | + } |
| 114 | + } |
99 | 115 |
|
| 116 | + pub fn as_value_suffix(self) -> Option<&'static str> { |
| 117 | + match self { |
| 118 | + KeyType::HUT => Some(".usage()"), |
| 119 | + KeyType::CG => Some(".into()"), |
| 120 | + _ => None |
| 121 | + } |
| 122 | + } |
| 123 | +} |
100 | 124 |
|
101 | | - std::fs::write( |
102 | | - format!("{output_path}generated.winput_to_enigo.rs"), |
103 | | - gen_convert( |
104 | | - &gen_template(CONVERT_GENERAL, "Vk", "Enigo", None, None), |
105 | | - csv.iter().map(|i| (i.winput, i.enigo)).filter(kv_is_valid) |
106 | | - ) |
107 | | - ).expect("Failed to write generated.rs"); |
| 125 | +fn is_valid<S: AsRef<str>>(s: S) -> bool { |
| 126 | + let s = s.as_ref().trim(); |
| 127 | + !s.is_empty() && !s.starts_with("n!") && !s.starts_with("na!") && !s.starts_with("todo!") && !s.starts_with("none!") |
| 128 | +} |
| 129 | +fn kv_is_valid<K: AsRef<str>, V: AsRef<str>>((k, v): &(K, V)) -> bool { |
| 130 | + is_valid(k) && is_valid(v) && !k.as_ref().trim().ends_with('*') |
| 131 | +} |
108 | 132 |
|
109 | | - std::fs::write( |
110 | | - format!("{output_path}generated.enigo_to_winput.rs"), |
111 | | - gen_convert( |
112 | | - &gen_template(CONVERT_GENERAL, "Enigo", "Vk", None, None), |
113 | | - csv.iter().map(|i| (i.enigo, i.winput)).filter(kv_is_valid) |
114 | | - ) |
115 | | - ).expect("Failed to write generated.rs"); |
| 133 | +struct Gen(KeyType, KeyType); |
116 | 134 |
|
117 | | - std::fs::write( |
118 | | - format!("{output_path}generated.enigo_to_vk.rs"), |
| 135 | +impl Gen { |
| 136 | + pub fn build(self, csv: &[Line<&str>]) -> String { |
| 137 | + let from = self.0; |
| 138 | + let to = self.1; |
119 | 139 | gen_convert( |
120 | | - &gen_template(CONVERT_GENERAL, "Enigo", "Vk", Some("keys::"), None), |
121 | | - csv.iter().map(|i| (i.enigo, i.vk)).filter(kv_is_valid) |
| 140 | + &gen_template(CONVERT_GENERAL, from.name(), to.name(), to.as_value_prefix(), to.as_value_suffix()), |
| 141 | + csv.iter().map(|i| (from.get_line(i), to.get_line(i))).filter(kv_is_valid) |
122 | 142 | ) |
123 | | - ).expect("Failed to write generated.rs"); |
| 143 | + } |
| 144 | +} |
124 | 145 |
|
125 | | - std::fs::write( |
126 | | - format!("{output_path}generated.winput_to_macos.rs"), |
127 | | - gen_convert( |
128 | | - &gen_template(CONVERT_GENERAL, "Vk", "CGKeyCode", None, Some(".into()")), |
129 | | - csv.iter().map(|i| (i.winput, i.cg)).filter(kv_is_valid) |
130 | | - ) |
131 | | - ).expect("Failed to write generated.rs"); |
| 146 | +fn save_file<P: AsRef<Path>, S: AsRef<str>>(filename: P, content: S) -> std::io::Result<()> { |
| 147 | + let filename = filename.as_ref(); |
| 148 | + let content = content.as_ref(); |
| 149 | + let path = filename.to_path_buf(); |
| 150 | + if content.is_empty() { |
| 151 | + return Err(std::io::Error::other("Content is empty")); |
| 152 | + } |
| 153 | + if path.exists() && !path.is_file() { |
| 154 | + return Err(std::io::Error::other(format!("{} is not a file", filename.display()))); |
| 155 | + } |
| 156 | + if path.exists() && !content.is_empty() { |
| 157 | + let existing_content = std::fs::read_to_string(&path)?; |
| 158 | + if existing_content == content { |
| 159 | + return Ok(()); |
| 160 | + } |
| 161 | + } |
| 162 | + std::fs::write(path, content) |
| 163 | +} |
| 164 | + |
| 165 | +fn main() { |
| 166 | + let csv_path = "src/keycodes/convert/convert.csv"; |
| 167 | + let output_path = "src/keycodes/convert"; |
| 168 | + |
| 169 | + if std::env::var("DOCS_RS").is_ok() { |
| 170 | + return; |
| 171 | + } |
132 | 172 |
|
133 | 173 | println!("cargo:rerun-if-changed=build.rs"); |
134 | 174 | println!("cargo:rerun-if-changed={csv_path}"); |
| 175 | + println!("cargo:rerun-if-env-changed=CARGO_FEATURE_GENERATING_CONVERT"); |
| 176 | + if std::env::var("CARGO_FEATURE_GENERATING_CONVERT").is_err() { |
| 177 | + return; |
| 178 | + } |
| 179 | + |
| 180 | + let csv_content = std::fs::read_to_string(csv_path).expect("Failed to read convert.csv"); |
| 181 | + let csv = csv_content.lines().filter(|i| !i.trim().is_empty()) |
| 182 | + .map(|i| i.split(',').collect::<Vec<_>>().into()).collect::<Vec<Line<_>>>(); |
| 183 | + |
| 184 | + for tuple in [ |
| 185 | + (KeyType::Enigo, KeyType::WinVk), |
| 186 | + (KeyType::Enigo, KeyType::Winput), |
| 187 | + (KeyType::Winput, KeyType::HUT), |
| 188 | + (KeyType::Winput, KeyType::Enigo), |
| 189 | + (KeyType::Winput, KeyType::CG), |
| 190 | + ] { |
| 191 | + let (from, to) = tuple; |
| 192 | + let filename = format!("generated.{from:?}_to_{to:?}.rs"); |
| 193 | + let content = Gen(from, to).build(&csv); |
| 194 | + save_file(format!("{output_path}/{filename}"), content) |
| 195 | + .expect("Failed to write generated.rs"); |
| 196 | + } |
135 | 197 | } |
0 commit comments