So after a very long and painful phone call to ASUS ™️ about a dead motherboard, I decided that knowing the NATO phonetic alphabet would have cut that phone call in half.
And since I’ve been meaning to learn rust for a while now, I decided why not learn both?
It took a while to figure out how to use a global static variable, because rust really doesn’t like that.
TL;DR:
./src/alphabet.rs (source)
// you may ask why I used `&'static str` and not just `String`,
// and the answer is that it was mostly because I could?
static ALPHABET: OnceLock<HashMap<char, &'static str>> = OnceLock::new();
fn init_alphabet() -> HashMap<char, &'static str> { ... }
pub fn get_alphabet() -> HashMap<char, &'static str> {
ALPHABET.get_or_init(init_alphabet).clone()
}
pub fn to_alphabet(s: String) -> Vec<(char, String)> {
let mut ret = Vec::<(char, String)>::new();
let alphabet = alphabet();
for c in s.to_lowercase().chars() {
let cstr = c.to_string();
let w = alphabet.get(&c).unwrap_or(&cstr.as_str()).to_string();
ret.push((c, w.to_string()));
}
ret
}
Once that was done, I couldn’t decide how to make the interface, so I went with a simple cli:
./src/main.rs (cli only)
mod alphabet;
fn main() {
// skip first arg, which is the program's name
for arg in std::env::args().skip(1) {
println!("-> {} <-", arg);
for (c, s) in alphabet::to_alphabet(arg) {
println!("{} -> {}", c, s);
}
}
}
And that worked fine, it’s simple and does that job.
cargo run -- hello world
-> hello <-
h -> hotel
e -> echo
l -> lima
l -> lima
o -> oscar
-> world <-
w -> whiskey
o -> oscar
r -> romeo
l -> lima
d -> delta
But I decided it needed to have a web interface, and since I’ve also been meaning to learn WASM, and I couldn’t sleep at the time, so I decided to write the interface in UI in rust/wasm.
I went with an excellent framework called yew, it’s very similar to ReactJS in a lot of way, and trunk to build the wasm/html.
Implementing the UI was very straight forward, just added an index.html
and index.scss
to the root of the project,
and then created ./src/app.rs
.
./src/app.rs (source)
#[function_component(App)]
pub fn app() -> Html {
let input_ref = use_node_ref();
let alphabet_value_handle = use_state(|| Vec::<(char, String)>::new());
let alphabet_value = (*alphabet_value_handle).clone();
let onchange = { .... }
html! {
<main>
<label for="words">
{"Words:"}
</label>
<input ref={input_ref} onchange={onchange} id="words" type="text"/>
<br />
<ul>
{alphabet_value.iter().map(|(c,s)| html! { <li><pre>{"`"}{c}{"` -> "}{s}</pre></li> }).collect::<Html>()}
</ul>
</main>
}
}
But I wanted to maintain the cli interface in the same program, after some googling and reading some rust docs, I ended up with:
./src/main.rs (source):
mod alphabet;
#[cfg(target_arch = "wasm32")]
mod app;
#[cfg(target_arch = "wasm32")]
fn main()
yew::Renderer::<app::App>::new().render();
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
// skip first arg, which is the program's name
for arg in std::env::args().skip(1) {
println!("-> {} <-", arg);
for (c, s) in alphabet::to_alphabet(arg) {
println!("{} -> {}", c, s);
}
}
}
And tada https://oneofone.dev/alphabet/ was born.