1use std::collections::HashMap;
2
3use crate::gnostic::openapi::v3::Operation;
4use crate::google::api::{FieldBehavior, HttpRule, ResourceDescriptor, ResourceReference};
5use crate::parsing::http::HttpPattern;
6use crate::parsing::types::{BaseType, UnifiedType};
7use crate::utils::last_segment;
8
9#[derive(Debug, Default)]
11pub struct CodeGenMetadata {
12 pub messages: HashMap<String, MessageInfo>,
13 pub enums: HashMap<String, EnumInfo>,
14 pub services: HashMap<String, ServiceInfo>,
15}
16
17impl CodeGenMetadata {
18 pub fn get_message_fields(&self, type_name: &str) -> Vec<MessageField> {
20 self.messages
21 .get(type_name)
22 .map(|msg| msg.fields.clone())
23 .unwrap_or_default()
24 }
25
26 pub fn resource_from_singular(&self, name: &str) -> Option<&ResourceDescriptor> {
28 self.messages.values().find_map(|info| {
29 info.resource_descriptor
30 .as_ref()
31 .filter(|r| r.singular == name)
32 })
33 }
34
35 pub fn resource_from_plural(&self, name: &str) -> Option<&ResourceDescriptor> {
37 self.messages.values().find_map(|info| {
38 info.resource_descriptor
39 .as_ref()
40 .filter(|r| r.plural == name)
41 })
42 }
43
44 pub fn get_resource_descriptor(&self, type_name: &str) -> Option<&ResourceDescriptor> {
46 if let Some(descriptor) = self
48 .messages
49 .get(type_name)
50 .and_then(|info| info.resource_descriptor.as_ref())
51 {
52 return Some(descriptor);
53 }
54
55 self.messages.iter().find_map(|(key, info)| {
57 if last_segment(key) == type_name {
58 info.resource_descriptor.as_ref()
59 } else {
60 None
61 }
62 })
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct MessageInfo {
69 pub name: String,
70 pub fields: Vec<MessageField>,
71 pub resource_descriptor: Option<ResourceDescriptor>,
72 pub documentation: Option<String>,
73}
74
75#[derive(Debug, Clone)]
77pub struct MessageField {
78 pub name: String,
79 pub json_name: Option<String>,
81 pub response_header: Option<String>,
83 pub request_header: Option<String>,
85 pub emit_unpopulated: bool,
87 pub raw_body: bool,
89 pub unified_type: UnifiedType,
91 pub documentation: Option<String>,
92 pub oneof_variants: Option<Vec<OneofVariant>>,
94 pub field_behavior: Vec<FieldBehavior>,
96 pub is_sensitive: bool,
99 pub resource_reference: Option<ResourceReference>,
106}
107
108#[derive(Debug, Clone)]
110pub struct OneofVariant {
111 pub field_name: String, pub variant_name: String, pub field_type: UnifiedType, pub documentation: Option<String>,
115}
116
117impl OneofVariant {
118 pub fn is_int32(&self) -> bool {
122 matches!(self.field_type.base_type, BaseType::Int32)
123 }
124}
125
126#[derive(Debug, Clone)]
128pub struct EnumInfo {
129 pub name: String,
130 pub values: Vec<EnumValue>,
131 pub documentation: Option<String>,
132}
133
134#[derive(Debug, Clone)]
136pub struct EnumValue {
137 pub name: String,
138 pub number: i32,
139 pub documentation: Option<String>,
140}
141
142#[derive(Debug, Clone)]
144pub struct ServiceInfo {
145 pub name: String,
146 pub package: String,
148 pub documentation: Option<String>,
149 pub methods: Vec<MethodMetadata>,
150}
151
152#[derive(Debug, Clone)]
154pub struct MethodMetadata {
155 pub service_name: String,
156 pub method_name: String,
157 pub input_type: String,
158 pub output_type: String,
159 pub operation: Option<Operation>,
160 pub http_rule: HttpRule,
161 pub http_pattern: HttpPattern,
163 pub documentation: Option<String>,
164 pub server_streaming: bool,
166}
167
168impl MethodMetadata {
169 pub fn http_method(&self) -> Option<&str> {
171 use crate::google::api::http_rule::Pattern;
172 self.http_rule.pattern.as_ref().map(|p| match p {
173 Pattern::Get(_) => "GET",
174 Pattern::Post(_) => "POST",
175 Pattern::Put(_) => "PUT",
176 Pattern::Delete(_) => "DELETE",
177 Pattern::Patch(_) => "PATCH",
178 Pattern::Custom(c) => c.kind.as_str(),
179 })
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_service_based_method_structure() {
189 let mut codegen_metadata = CodeGenMetadata {
190 messages: HashMap::new(),
191 services: HashMap::new(),
192 enums: HashMap::new(),
193 };
194
195 let mut service_info = ServiceInfo {
197 name: "TestService".to_string(),
198 package: "test.v1".to_string(),
199 documentation: Some("Test service documentation".to_string()),
200 methods: Vec::new(),
201 };
202
203 let http_rule = crate::google::api::HttpRule {
205 pattern: Some(crate::google::api::http_rule::Pattern::Get(
206 "/test".to_string(),
207 )),
208 ..Default::default()
209 };
210
211 let method = MethodMetadata {
212 service_name: "TestService".to_string(),
213 method_name: "TestMethod".to_string(),
214 input_type: ".test.TestRequest".to_string(),
215 output_type: ".test.TestResponse".to_string(),
216 server_streaming: false,
217 operation: None,
218 http_pattern: crate::parsing::http::HttpPattern::parse("/test"),
219 http_rule,
220 documentation: Some("Test method documentation".to_string()),
221 };
222 service_info.methods.push(method);
223
224 codegen_metadata
225 .services
226 .insert("TestService".to_string(), service_info);
227
228 let service = codegen_metadata.services.get("TestService").unwrap();
230 assert_eq!(service.name, "TestService");
231 assert_eq!(service.methods.len(), 1);
232 assert_eq!(service.methods[0].method_name, "TestMethod");
233 }
234}