heck_string_cli/
convert.rs1use std::fmt::{Display, Formatter};
2
3use clap::builder::PossibleValue;
4use clap::ValueEnum;
5use heck::{
6 ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase,
7 ToShoutySnakeCase, ToSnakeCase, ToTrainCase, ToTitleCase
8};
9
10#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
12pub enum ToCase {
13 KebabCase,
14 CamelCase,
15 PascalCase,
16 ShoutyKebabCase,
17 ShoutySnakeCase,
18 SnakeCase,
19 TrainCase,
20 TitleCase,
21}
22impl Display for ToCase {
23 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
24 let variant = self.variant_name().to_kebab_case();
25 write!(f, "{variant}")
26 }
27}
28impl ToCase {
29 pub fn variant_name(&self) -> &'static str {
30 match self {
31 ToCase::KebabCase => "Kebab",
32 ToCase::CamelCase => "Camel",
33 ToCase::PascalCase => "Pascal",
34 ToCase::ShoutyKebabCase => "ShoutyKebab",
35 ToCase::ShoutySnakeCase => "ShoutySnake",
36 ToCase::SnakeCase => "Snake",
37 ToCase::TrainCase => "Train",
38 ToCase::TitleCase => "Title",
39 }
40 }
41
42 pub fn convert<T: Display>(&self, input: T) -> String {
43 let string = input.to_string();
44 match self {
45 ToCase::KebabCase => string.to_kebab_case(),
46 ToCase::CamelCase => string.to_lower_camel_case(),
47 ToCase::PascalCase => string.to_pascal_case(),
48 ToCase::ShoutyKebabCase => string.to_shouty_kebab_case(),
49 ToCase::ShoutySnakeCase => string.to_shouty_snake_case(),
50 ToCase::SnakeCase => string.to_snake_case(),
51 ToCase::TrainCase => string.to_train_case(),
52 ToCase::TitleCase => string.to_title_case(),
53 }
54 }
55
56 pub fn variant_name_with_to_prefix(&self) -> String {
57 let variant = self.variant_name();
58 format!("To{variant}")
59 }
60
61 pub fn variant_name_with_case_suffix(&self) -> String {
62 let variant = self.variant_name();
63 format!("{variant}Case")
64 }
65
66 pub fn variant_name_with_prefix_and_suffix(&self) -> String {
67 let variant = self.variant_name();
68 format!("To{variant}Case")
69 }
70
71 pub fn base_variant_names(&self) -> [String; 4] {
72 [
73 self.variant_name().to_string(),
74 self.variant_name_with_to_prefix(),
75 self.variant_name_with_case_suffix(),
76 self.variant_name_with_prefix_and_suffix(),
77 ]
78 }
79
80 pub fn variant_names(&self) -> Vec<String> {
81 let mut variants = Vec::<String>::new();
82
83 for variant in self
84 .base_variant_names()
85 .into_iter()
86 .map(|variant| {
87 [
88 variant.to_kebab_case(),
89 variant.to_lower_camel_case(),
90 variant.to_pascal_case(),
91 variant.to_train_case(),
92 variant.to_title_case(),
93 variant.to_snake_case(),
94 variant.to_shouty_snake_case(),
95 variant.to_shouty_kebab_case(),
96 ]
97 .to_vec()
98 })
99 .flatten()
100 {
101 if !variants.contains(&variant) {
102 variants.push(variant);
103 }
104 }
105 variants
106 }
107
108 pub fn variants<'a>() -> &'a [ToCase] {
109 &[
110 ToCase::KebabCase,
111 ToCase::CamelCase,
112 ToCase::PascalCase,
113 ToCase::ShoutyKebabCase,
114 ToCase::ShoutySnakeCase,
115 ToCase::SnakeCase,
116 ToCase::TrainCase,
117 ToCase::TitleCase,
118 ]
119 }
120}
121impl ValueEnum for ToCase {
122 fn value_variants<'a>() -> &'a [ToCase] {
123 ToCase::variants()
124 }
125
126 fn to_possible_value(&self) -> Option<PossibleValue> {
127 let variants = self.variant_names();
128 let mut pv = PossibleValue::new(variants[0].to_string());
129 for variant in &variants[1..] {
130 pv = pv.alias(variant);
131 }
132 Some(pv)
133 }
134
135 fn from_str(
136 val: &str,
137 ignore_case: bool,
138 ) -> std::result::Result<ToCase, String> {
139 let val = if ignore_case {
140 val.to_lowercase()
141 } else {
142 val.to_string()
143 };
144 let val = val.trim();
145 for variant in ToCase::variants() {
146 for variant_name in
147 variant
148 .variant_names()
149 .into_iter()
150 .map(|variant| {
151 if ignore_case {
152 variant.to_lowercase().to_string()
153 } else {
154 variant.to_string()
155 }
156 })
157 {
158 if val.to_string() == variant_name {
159 return Ok(variant.clone());
160 }
161 }
162 }
163 return Err(val.to_string());
164 }
165}