Skip to main content

olai_codegen/parsing/
mod.rs

1use std::collections::HashMap;
2
3pub use self::http::{HttpPattern, UrlSegment, extract_http_rule_pattern, extract_path_parameters};
4pub use self::models::*;
5use protobuf::descriptor::{FileDescriptorProto, FileDescriptorSet, SourceCodeInfo};
6pub mod types;
7use crate::Result;
8
9mod enum_parser;
10pub mod http;
11mod message;
12mod models;
13mod service;
14
15/// Extract documentation text for a protobuf element at the given source path.
16///
17/// Scans `SourceCodeInfo.location` for an entry whose path matches `path`,
18/// prefers leading comments over trailing comments, and returns the trimmed text.
19pub(super) fn extract_documentation(sci: Option<&SourceCodeInfo>, path: &[i32]) -> Option<String> {
20    let sci = sci?;
21    for location in &sci.location {
22        if location.path.as_slice() == path {
23            let text = if location.has_leading_comments() {
24                location.leading_comments().trim().to_string()
25            } else if location.has_trailing_comments() {
26                location.trailing_comments().trim().to_string()
27            } else {
28                String::new()
29            };
30            if !text.is_empty() {
31                return Some(text);
32            }
33        }
34    }
35    None
36}
37
38// Known extension field numbers
39const GOOGLE_API_HTTP_EXTENSION: u32 = 72295728; // google.api.http
40const GNOSTIC_OPERATION_EXTENSION: u32 = 1143; // gnostic.openapi.v3.operation
41const GOOGLE_API_RESOURCE_EXTENSION: u32 = 1053; // google.api.resource
42const GOOGLE_API_FIELD_BEHAVIOR_EXTENSION: u32 = 1052; // google.api.field_behavior
43const GOOGLE_API_RESOURCE_REFERENCE_EXTENSION: u32 = 1055; // google.api.resource_reference
44
45pub fn parse_file_descriptor_set(
46    file_descriptor_set: &FileDescriptorSet,
47) -> Result<CodeGenMetadata> {
48    let mut codegen_metadata = CodeGenMetadata {
49        messages: HashMap::new(),
50        enums: HashMap::new(),
51        services: HashMap::new(),
52    };
53
54    // Process each file descriptor
55    for file_descriptor in &file_descriptor_set.file {
56        process_file_descriptor(file_descriptor, &mut codegen_metadata)?;
57    }
58
59    Ok(codegen_metadata)
60}
61
62/// Process a single protobuf file descriptor
63///
64/// Extracts all messages, services, and annotations from the file.
65/// Collects metadata for code generation.
66pub fn process_file_descriptor(
67    file_desc: &FileDescriptorProto,
68    codegen_metadata: &mut CodeGenMetadata,
69) -> Result<()> {
70    let file_name = file_desc.name();
71
72    // Extract source code info for documentation
73    let source_code_info = file_desc.source_code_info.as_ref();
74
75    // Process enums in the file
76    for (enum_index, enum_desc) in file_desc.enum_type.iter().enumerate() {
77        let package_name = file_desc.package();
78        let type_prefix = if package_name.is_empty() {
79            String::new()
80        } else {
81            format!(".{}", package_name)
82        };
83        enum_parser::process_enum(
84            enum_desc,
85            codegen_metadata,
86            &type_prefix,
87            source_code_info,
88            &[5, enum_index as i32], // enum_type is field 5 in FileDescriptorProto
89        )?;
90    }
91
92    // Process messages in the file
93    for (message_index, message) in file_desc.message_type.iter().enumerate() {
94        let package_name = file_desc.package();
95        let type_prefix = if package_name.is_empty() {
96            String::new()
97        } else {
98            format!(".{}", package_name)
99        };
100
101        message::process_message(
102            message,
103            file_name,
104            codegen_metadata,
105            &type_prefix,
106            source_code_info,
107            &[4, message_index as i32], // message_type is field 4 in FileDescriptorProto
108        )?;
109    }
110
111    // Process services in the file
112    let package_name = file_desc.package().to_string();
113    for (service_index, service) in file_desc.service.iter().enumerate() {
114        service::process_service(
115            service,
116            &package_name,
117            codegen_metadata,
118            source_code_info,
119            service_index,
120        )?;
121    }
122
123    Ok(())
124}