1use anyhow::anyhow;
2use wowlab_fs::path::Path;
3use wowlab_types::game::SpecId;
4
5pub(crate) fn to_snake(name: &str) -> String {
7 name.to_lowercase()
8}
9
10pub(crate) fn to_rotation_key(name: &str) -> String {
12 to_snake(name).replace("_s_", "s_")
13}
14
15fn title_case(s: &str, lowercase_tail: bool) -> String {
16 s.split('_')
17 .map(|w| {
18 let mut chars = w.chars();
19
20 match chars.next() {
21 None => String::new(),
22 Some(c) => {
23 let mut out = c.to_uppercase().to_string();
24
25 if lowercase_tail {
26 out.extend(chars.map(|ch| ch.to_ascii_lowercase()));
27 } else {
28 out.extend(chars);
29 }
30
31 out
32 }
33 }
34 })
35 .collect::<Vec<_>>()
36 .join(" ")
37}
38
39pub(crate) fn to_display_name(name: &str) -> String {
41 title_case(name, true)
42}
43
44pub(crate) fn spec_from_wow_id(wow_id: u32) -> anyhow::Result<SpecId> {
46 SpecId::from_wow_spec_id(wow_id).ok_or_else(|| anyhow!("unknown WoW spec ID: {wow_id}"))
47}
48
49pub(crate) fn display_name_from_filename(path: &Path) -> anyhow::Result<String> {
51 let stem = path
52 .file_stem()
53 .ok_or_else(|| anyhow!("manifest path has no filename: {}", path.display()))?
54 .to_string_lossy();
55
56 Ok(title_case(&stem, false))
57}
58
59pub(crate) fn module_name_from_filename(path: &Path) -> anyhow::Result<String> {
61 Ok(path
62 .file_stem()
63 .ok_or_else(|| anyhow!("manifest path has no filename: {}", path.display()))?
64 .to_string_lossy()
65 .into_owned())
66}
67
68pub(crate) fn fmt_f64(v: f64) -> String {
70 if v.fract().abs() < f64::EPSILON && v.abs() < 1e15 {
71 format!("{v:.1}")
72 } else {
73 format!("{v}")
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use googletest::prelude::*;
80
81 use super::*;
82
83 #[gtest]
84 fn rotation_key_matches_simc_possessive_token() -> Result<()> {
85 verify_that!(to_rotation_key("GOREMAW_S_BITE"), eq("goremaws_bite"))?;
86
87 verify_that!(to_rotation_key("REAVERS_GLAIVE"), eq("reavers_glaive"))
88 }
89
90 #[gtest]
91 fn manifest_name_helpers_reject_paths_without_filenames() -> Result<()> {
92 let path = Path::new("/");
93
94 let display_error = display_name_from_filename(path).err().or_fail()?;
95 let module_error = module_name_from_filename(path).err().or_fail()?;
96
97 verify_that!(
98 display_error.to_string(),
99 eq("manifest path has no filename: /")
100 )?;
101
102 verify_that!(
103 module_error.to_string(),
104 eq("manifest path has no filename: /")
105 )
106 }
107}