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