1use convert_case::{Case, Casing};
6
7pub fn extract_simple_type_name(name: &str) -> String {
12 name.split('.').next_back().unwrap_or(name).to_string()
13}
14
15pub fn extract_qualified_type_name(name: &str) -> String {
29 let segments: Vec<&str> = name.split('.').filter(|s| !s.is_empty()).collect();
30 let start = segments
33 .iter()
34 .position(|s| s.starts_with(|c: char| c.is_ascii_uppercase()));
35 match start {
36 Some(i) => segments[i..].concat(),
37 None => extract_simple_type_name(name),
39 }
40}
41
42pub mod strings {
44 use super::*;
45
46 pub fn service_to_handler_name(service_name: &str) -> String {
49 if let Some(base) = service_name.strip_suffix("Service") {
50 format!("{}Handler", singularize(base))
51 } else {
52 format!("{}Handler", service_name)
53 }
54 }
55
56 fn singularize(word: &str) -> String {
61 if let Some(stem) = word.strip_suffix("ies") {
62 format!("{stem}y")
63 } else {
64 word.trim_end_matches('s').to_string()
65 }
66 }
67
68 pub fn operation_to_method_name(operation_id: &str) -> String {
71 operation_id.to_case(Case::Snake)
72 }
73
74 pub fn service_to_base_path(service_name: &str) -> String {
77 if let Some(base) = service_name.strip_suffix("Service") {
78 base.to_case(Case::Snake)
79 } else {
80 service_name.to_case(Case::Snake)
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 mod string_tests {
90 use super::*;
91
92 #[test]
93 fn test_service_to_handler_name() {
94 assert_eq!(
95 strings::service_to_handler_name("CatalogsService"),
96 "CatalogHandler"
97 );
98 assert_eq!(
99 strings::service_to_handler_name("RecipientsService"),
100 "RecipientHandler"
101 );
102 assert_eq!(
103 strings::service_to_handler_name("SchemasService"),
104 "SchemaHandler"
105 );
106 assert_eq!(
107 strings::service_to_handler_name("TagPoliciesService"),
108 "TagPolicyHandler"
109 );
110 }
111
112 #[test]
113 fn test_operation_to_method_name() {
114 assert_eq!(
115 strings::operation_to_method_name("ListCatalogs"),
116 "list_catalogs"
117 );
118 assert_eq!(
119 strings::operation_to_method_name("CreateCatalog"),
120 "create_catalog"
121 );
122 assert_eq!(
123 strings::operation_to_method_name("GetCatalog"),
124 "get_catalog"
125 );
126 }
127
128 #[test]
129 fn test_service_to_base_path() {
130 assert_eq!(strings::service_to_base_path("CatalogsService"), "catalogs");
131 assert_eq!(
132 strings::service_to_base_path("RecipientsService"),
133 "recipients"
134 );
135 }
136 }
137
138 mod qualified_type_name {
139 use super::*;
140
141 #[test]
142 fn top_level_type_is_unchanged() {
143 assert_eq!(
144 extract_qualified_type_name(".unitycatalog.catalog.v1.Catalog"),
145 "Catalog"
146 );
147 assert_eq!(extract_qualified_type_name(".pkg.v1.UpdateX"), "UpdateX");
148 }
149
150 #[test]
151 fn nested_type_is_prefixed_by_parent() {
152 assert_eq!(
153 extract_qualified_type_name(
154 ".unitycatalog.temporary_credentials.v1.GenerateTemporaryTableCredentialsRequest.Operation"
155 ),
156 "GenerateTemporaryTableCredentialsRequestOperation"
157 );
158 }
159
160 #[test]
161 fn distinct_parents_yield_distinct_names() {
162 let a = extract_qualified_type_name(".pkg.v1.GenerateTableCredsRequest.Operation");
163 let b = extract_qualified_type_name(".pkg.v1.GenerateVolumeCredsRequest.Operation");
164 assert_ne!(a, b);
165 }
166
167 #[test]
168 fn no_leading_dot_or_package() {
169 assert_eq!(extract_qualified_type_name("Catalog"), "Catalog");
171 }
172 }
173}