Skip to content

Commit

Permalink
add our own proto_parse
Browse files Browse the repository at this point in the history
it's not yet perfect but it's good start
  • Loading branch information
j03-dev committed Aug 30, 2024
1 parent 1aabc5e commit f79375e
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 59 deletions.
19 changes: 0 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions libs/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ itertools = "0.11.0"
colored = "2.0.4"
indoc.workspace = true
thiserror.workspace = true

protobuf = "3.5.0"
protobuf-parse = "3.5.1"
protobuf = "3.5.1"
25 changes: 3 additions & 22 deletions libs/common/src/grpc.rs → libs/common/src/grpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,15 @@
// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

use anyhow::{Context, Result};
pub mod proto_parser;

use std::path::Path;
use anyhow::{Context, Result};

use protobuf::{
descriptor::{FileDescriptorProto, MethodDescriptorProto},
descriptor::MethodDescriptorProto,
reflect::{FieldDescriptor, FileDescriptor},
};

pub fn get_file_descriptor(proto_file: &Path) -> Result<FileDescriptor> {
let proto_folder = proto_file
.parent()
.context("Proto file is not within a folder")?;

let mut file_descriptors_protos = protobuf_parse::Parser::new()
.include(proto_folder)
.input(proto_file)
.parse_and_typecheck()
.unwrap()
.file_descriptors;

let file_descriptor_proto: FileDescriptorProto = file_descriptors_protos.pop().unwrap();

let file_descriptor = FileDescriptor::new_dynamic(file_descriptor_proto, &[])?;

Ok(file_descriptor)
}

pub fn get_method_descriptor_proto(
file_descriptor: FileDescriptor,
method_name: &str,
Expand Down
167 changes: 167 additions & 0 deletions libs/common/src/grpc/proto_parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

use anyhow::{anyhow, Result};
use protobuf::{
descriptor::{
field_descriptor_proto::Type, DescriptorProto, EnumDescriptorProto,
EnumValueDescriptorProto, FieldDescriptorProto, FileDescriptorProto,
},
reflect::FileDescriptor,
};

pub struct ProtoParser;

impl ProtoParser {
pub fn parse(content: &str) -> Result<FileDescriptorProto> {
let mut file_descriptor = FileDescriptorProto::new();
let mut current_message: Option<DescriptorProto> = None;
let mut current_enum: Option<EnumDescriptorProto> = None;

for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with("//") {
continue;
}

if line.starts_with("syntax") {
Self::parse_syntax(&mut file_descriptor, line)?;
} else if line.starts_with("package") {
Self::parse_package(&mut file_descriptor, line)?;
} else if line.starts_with("message") {
if let Some(message) = current_message.take() {
file_descriptor.message_type.push(message);
}
current_message = Some(Self::parse_message_start(line)?);
} else if line.starts_with("enum") {
if let Some(enum_type) = current_enum.take() {
file_descriptor.enum_type.push(enum_type);
}
current_enum = Some(Self::parse_enum_start(line)?);
} else if line.ends_with('}') {
if let Some(message) = current_message.take() {
file_descriptor.message_type.push(message);
}
if let Some(enum_type) = current_enum.take() {
file_descriptor.enum_type.push(enum_type);
}
} else if let Some(ref mut message) = current_message {
Self::parse_message_field(message, line)?;
} else if let Some(ref mut enum_type) = current_enum {
Self::parse_enum_value(enum_type, line)?;
}
}

if let Some(message) = current_message.take() {
file_descriptor.message_type.push(message);
}
if let Some(enum_type) = current_enum.take() {
file_descriptor.enum_type.push(enum_type);
}

Ok(file_descriptor)
}

fn parse_syntax(file_descriptor: &mut FileDescriptorProto, line: &str) -> Result<()> {
let syntax = line
.split('=')
.nth(1)
.ok_or_else(|| anyhow!("Invalid syntax line"))?
.trim()
.trim_matches('"');
file_descriptor.set_syntax(syntax.to_string());
Ok(())
}

fn parse_package(file_descriptor: &mut FileDescriptorProto, line: &str) -> Result<()> {
let package = line
.split_whitespace()
.nth(1)
.ok_or_else(|| anyhow!("Invalid package line"))?
.trim_matches(';');
file_descriptor.set_package(package.to_string());
Ok(())
}

fn parse_message_start(line: &str) -> Result<DescriptorProto> {
let name = line
.split_whitespace()
.nth(1)
.ok_or_else(|| anyhow!("Invalid message line"))?
.trim_matches('{');
let mut message = DescriptorProto::new();
message.set_name(name.to_string());
Ok(message)
}

fn parse_message_field(message: &mut DescriptorProto, line: &str) -> Result<()> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 4 {
return Err(anyhow!("Invalid field line"));
}

let mut field = FieldDescriptorProto::new();
field.set_name(parts[1].to_string());
field.set_number(parts[3].trim_matches(';').parse()?);

let field_type = match parts[0] {
"double" => Type::TYPE_DOUBLE,
"float" => Type::TYPE_FLOAT,
"int64" => Type::TYPE_INT64,
"uint64" => Type::TYPE_UINT64,
"int32" => Type::TYPE_INT32,
"fixed64" => Type::TYPE_FIXED64,
"fixed32" => Type::TYPE_FIXED32,
"bool" => Type::TYPE_BOOL,
"string" => Type::TYPE_STRING,
"group" => Type::TYPE_GROUP,
"message" => Type::TYPE_MESSAGE,
"bytes" => Type::TYPE_BYTES,
"uint32" => Type::TYPE_UINT32,
"enum" => Type::TYPE_ENUM,
"sfixed32" => Type::TYPE_SFIXED32,
"sfixed64" => Type::TYPE_SFIXED64,
"sint32" => Type::TYPE_SINT32,
"sint64" => Type::TYPE_SINT64,
_ => return Err(anyhow!("Unsupported field type: {}", parts[0])),
};
field.set_type(field_type);

message.field.push(field);
Ok(())
}

fn parse_enum_start(line: &str) -> Result<EnumDescriptorProto> {
let name = line
.split_whitespace()
.nth(1)
.ok_or_else(|| anyhow!("Invalid enum line"))?
.trim_matches('{');
let mut enum_type = EnumDescriptorProto::new();
enum_type.set_name(name.to_string());
Ok(enum_type)
}

fn parse_enum_value(enum_type: &mut EnumDescriptorProto, line: &str) -> Result<()> {
let parts: Vec<&str> = line.split('=').collect();
if parts.len() != 2 {
return Err(anyhow!("Invalid enum value line"));
}

let name = parts[0].trim().to_string();
let number: i32 = parts[1].trim().trim_matches(';').parse()?;

let mut enum_value = EnumValueDescriptorProto::new();
enum_value.set_name(name);
enum_value.set_number(number);

enum_type.value.push(enum_value);
Ok(())
}
}

pub fn get_file_descriptor(content: &str) -> Result<FileDescriptor> {
let file_descriptor_proto = ProtoParser::parse(content)?;
let file_descriptor = FileDescriptor::new_dynamic(file_descriptor_proto, &[])?;
Ok(file_descriptor)
}
2 changes: 1 addition & 1 deletion typegate/engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ shadow-rs.workspace = true

tonic = "0.12.1"
bytes = "1.7.1"
protobuf = "3.5.0"
protobuf = "3.5.1"
protobuf-json-mapping = "3.5.1"

[dev-dependencies]
Expand Down
10 changes: 4 additions & 6 deletions typegate/engine/src/runtimes/grpc.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

use std::{cell::RefCell, ops::Deref, path::PathBuf, rc::Rc, str::FromStr, sync::Arc};
use std::{cell::RefCell, ops::Deref, rc::Rc, str::FromStr, sync::Arc};

use common::grpc::{
get_file_descriptor, get_method_descriptor_proto, get_relative_message_name,
get_relative_method_name,
get_method_descriptor_proto, get_relative_message_name, get_relative_method_name,
proto_parser::get_file_descriptor,
};

use bytes::{Buf, BufMut};
Expand Down Expand Up @@ -197,9 +197,7 @@ pub async fn op_call_grpc_method(
.get_mut(&input.client_id)
.with_context(|| format!("Could not find gRPC client '{}'", &input.client_id))?;

let path = PathBuf::from_str(&grpc_client.proto_file)?;

let file_descriptor = get_file_descriptor(&path)?;
let file_descriptor = get_file_descriptor(&grpc_client.proto_file)?;

let method_name = get_relative_method_name(&input.method)?;

Expand Down
2 changes: 0 additions & 2 deletions typegraph/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ ordered-float = "4.2.0"
glob = "0.3.1"
unindent = "0.2.3"

protobuf = "3.5.1"
protobuf-parse = "3.5.1"

[dev-dependencies]
insta = { version = "1.39.0", features = ["glob"] }
Expand Down
9 changes: 3 additions & 6 deletions typegraph/core/src/runtimes/grpc/type_generation.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0

use std::path::Path;

use anyhow::Result;
use common::grpc::{
get_file_descriptor, get_message_field_descriptor, get_method_descriptor_proto,
get_relative_method_name, Fields,
get_message_field_descriptor, get_method_descriptor_proto, get_relative_method_name,
proto_parser::get_file_descriptor, Fields,
};

use crate::{
Expand All @@ -20,8 +18,7 @@ pub struct Type {
}

pub fn generate_type(proto_file: &str, method_name: &str) -> Result<Type> {
let proto_path = Path::new(proto_file);
let file_descriptor = get_file_descriptor(proto_path)?;
let file_descriptor = get_file_descriptor(proto_file)?;

let method_descriptor = get_method_descriptor_proto(file_descriptor.clone(), method_name)?;

Expand Down

0 comments on commit f79375e

Please sign in to comment.