feat: add grpc support
This commit is contained in:
@@ -8,6 +8,8 @@ version.workspace = true
|
||||
[dependencies]
|
||||
mcpaas-core = { path = "../mcpaas-core" }
|
||||
mcpaas-schema = { path = "../mcpaas-schema" }
|
||||
prost.workspace = true
|
||||
prost-reflect.workspace = true
|
||||
prost-types.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
@@ -4,4 +4,8 @@ use thiserror::Error;
|
||||
pub enum ProtoError {
|
||||
#[error("oneof {oneof_name} must contain at least one variant")]
|
||||
EmptyOneof { oneof_name: String },
|
||||
#[error("descriptor set could not be decoded")]
|
||||
InvalidDescriptorSet,
|
||||
#[error("descriptor pool could not be built")]
|
||||
InvalidDescriptorPool,
|
||||
}
|
||||
|
||||
@@ -2,9 +2,141 @@ pub mod convert;
|
||||
pub mod errors;
|
||||
pub mod model;
|
||||
|
||||
use prost::Message;
|
||||
use prost_reflect::{
|
||||
Cardinality, DescriptorPool, EnumDescriptor, FieldDescriptor, Kind, MessageDescriptor,
|
||||
MethodDescriptor, ServiceDescriptor,
|
||||
};
|
||||
use prost_types::FileDescriptorSet;
|
||||
|
||||
pub use convert::to_schema::message_to_schema;
|
||||
pub use errors::ProtoError;
|
||||
pub use model::{
|
||||
ProtoEnum, ProtoField, ProtoFieldCardinality, ProtoFieldType, ProtoMapField, ProtoMessage,
|
||||
ProtoMethod, ProtoOneof, ProtoScalarKind, ProtoService,
|
||||
};
|
||||
|
||||
pub fn services_from_descriptor_set_bytes(bytes: &[u8]) -> Result<Vec<ProtoService>, ProtoError> {
|
||||
let descriptor_set =
|
||||
FileDescriptorSet::decode(bytes).map_err(|_| ProtoError::InvalidDescriptorSet)?;
|
||||
let pool = DescriptorPool::from_file_descriptor_set(descriptor_set)
|
||||
.map_err(|_| ProtoError::InvalidDescriptorPool)?;
|
||||
|
||||
Ok(pool.services().map(service_from_descriptor).collect())
|
||||
}
|
||||
|
||||
fn service_from_descriptor(service: ServiceDescriptor) -> ProtoService {
|
||||
ProtoService {
|
||||
package: service.parent_file().package_name().to_owned(),
|
||||
name: service.name().to_owned(),
|
||||
methods: service.methods().map(method_from_descriptor).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn method_from_descriptor(method: MethodDescriptor) -> ProtoMethod {
|
||||
ProtoMethod {
|
||||
name: method.name().to_owned(),
|
||||
input: message_from_descriptor(method.input()),
|
||||
output: message_from_descriptor(method.output()),
|
||||
client_streaming: method.is_client_streaming(),
|
||||
server_streaming: method.is_server_streaming(),
|
||||
}
|
||||
}
|
||||
|
||||
fn message_from_descriptor(message: MessageDescriptor) -> ProtoMessage {
|
||||
let fields = message
|
||||
.fields()
|
||||
.filter(|field| field.containing_oneof().is_none())
|
||||
.map(field_from_descriptor)
|
||||
.collect();
|
||||
let oneofs = message
|
||||
.oneofs()
|
||||
.map(|oneof| ProtoOneof {
|
||||
name: oneof.name().to_owned(),
|
||||
variants: oneof.fields().map(field_from_descriptor).collect(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
ProtoMessage {
|
||||
name: message.name().to_owned(),
|
||||
fields,
|
||||
oneofs,
|
||||
}
|
||||
}
|
||||
|
||||
fn field_from_descriptor(field: FieldDescriptor) -> ProtoField {
|
||||
let cardinality = if field.is_list() && !field.is_map() {
|
||||
ProtoFieldCardinality::Repeated
|
||||
} else {
|
||||
match field.cardinality() {
|
||||
Cardinality::Optional => ProtoFieldCardinality::Optional,
|
||||
Cardinality::Required => ProtoFieldCardinality::Required,
|
||||
Cardinality::Repeated => ProtoFieldCardinality::Repeated,
|
||||
}
|
||||
};
|
||||
|
||||
ProtoField {
|
||||
name: field.name().to_owned(),
|
||||
json_name: field.json_name().to_owned(),
|
||||
cardinality,
|
||||
field_type: field_type_from_descriptor(field),
|
||||
}
|
||||
}
|
||||
|
||||
fn field_type_from_descriptor(field: FieldDescriptor) -> ProtoFieldType {
|
||||
if field.is_map() {
|
||||
return map_field_from_descriptor(field);
|
||||
}
|
||||
|
||||
match field.kind() {
|
||||
Kind::Bool => scalar_field(ProtoScalarKind::Bool),
|
||||
Kind::String => scalar_field(ProtoScalarKind::String),
|
||||
Kind::Bytes => scalar_field(ProtoScalarKind::Bytes),
|
||||
Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => scalar_field(ProtoScalarKind::Int32),
|
||||
Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => scalar_field(ProtoScalarKind::Int64),
|
||||
Kind::Uint32 | Kind::Fixed32 => scalar_field(ProtoScalarKind::Uint32),
|
||||
Kind::Uint64 | Kind::Fixed64 => scalar_field(ProtoScalarKind::Uint64),
|
||||
Kind::Float => scalar_field(ProtoScalarKind::Float),
|
||||
Kind::Double => scalar_field(ProtoScalarKind::Double),
|
||||
Kind::Message(message) => ProtoFieldType::Message {
|
||||
message: Box::new(message_from_descriptor(message)),
|
||||
},
|
||||
Kind::Enum(enumeration) => ProtoFieldType::Enum {
|
||||
enumeration: enum_from_descriptor(enumeration),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn map_field_from_descriptor(field: FieldDescriptor) -> ProtoFieldType {
|
||||
let message = match field.kind() {
|
||||
Kind::Message(message) => message,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let key_field = message.map_entry_key_field();
|
||||
let value_field = message.map_entry_value_field();
|
||||
let key = match field_type_from_descriptor(key_field) {
|
||||
ProtoFieldType::Scalar { scalar } => scalar,
|
||||
_ => ProtoScalarKind::String,
|
||||
};
|
||||
|
||||
ProtoFieldType::Map {
|
||||
map: ProtoMapField {
|
||||
key,
|
||||
value: Box::new(field_type_from_descriptor(value_field)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn scalar_field(scalar: ProtoScalarKind) -> ProtoFieldType {
|
||||
ProtoFieldType::Scalar { scalar }
|
||||
}
|
||||
|
||||
fn enum_from_descriptor(enumeration: EnumDescriptor) -> ProtoEnum {
|
||||
ProtoEnum {
|
||||
name: enumeration.name().to_owned(),
|
||||
values: enumeration
|
||||
.values()
|
||||
.map(|value| value.name().to_owned())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user