wobbly dobbly impl of proto->wit
This commit is contained in:
parent
5801f4e724
commit
f84b496cc1
@ -4,11 +4,6 @@ package core.todo.v1;
|
||||
|
||||
message TodoListRequest {string user_id = 1;}
|
||||
|
||||
enum Error {
|
||||
UNKNOWN = 0;
|
||||
NOT_FOUND = 1;
|
||||
}
|
||||
|
||||
message TodoAddRequest {
|
||||
string user_id = 1;
|
||||
string task = 2;
|
||||
|
@ -74,7 +74,7 @@ pub struct Function {
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||
pub struct ReturnTy {
|
||||
pub return_type: String,
|
||||
pub error_type: String,
|
||||
pub error_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||
|
@ -170,7 +170,7 @@ impl Function {
|
||||
};
|
||||
|
||||
format!(
|
||||
"func {}({}){}",
|
||||
"{}: func({}){};",
|
||||
generate_wit_name(&self.name),
|
||||
params,
|
||||
return_type
|
||||
@ -180,14 +180,10 @@ impl Function {
|
||||
|
||||
impl ReturnTy {
|
||||
pub fn to_wit(&self) -> String {
|
||||
if self.error_type.is_empty() {
|
||||
self.return_type.clone()
|
||||
if let Some(err) = self.error_type.as_ref() {
|
||||
format!("result<{}, {}>", self.return_type, err)
|
||||
} else {
|
||||
format!(
|
||||
"result<{}, {}>",
|
||||
self.return_type,
|
||||
self.error_type
|
||||
)
|
||||
format!("option<{}>", self.return_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,21 @@ pub enum WitType {
|
||||
}
|
||||
|
||||
impl WitType {
|
||||
pub fn from_primitive_proto_type(proto_ty: &str) -> Valid<Self, anyhow::Error, anyhow::Error> {
|
||||
let binding = proto_ty.to_lowercase();
|
||||
let ty = binding.strip_prefix("type_").unwrap_or(proto_ty);
|
||||
match ty {
|
||||
"double" | "float" => Valid::succeed(WitType::Float64),
|
||||
"int32" | "sint32" | "fixed32" | "sfixed32" => Valid::succeed(WitType::S32),
|
||||
"int64" | "sint64" | "fixed64" | "sfixed64" => Valid::succeed(WitType::S64),
|
||||
"uint32" => Valid::succeed(WitType::U32),
|
||||
"uint64" => Valid::succeed(WitType::U64),
|
||||
"bool" => Valid::succeed(WitType::Bool),
|
||||
"string" => Valid::succeed(WitType::String),
|
||||
// Ideally, this should never be reached
|
||||
_ => Valid::fail(anyhow::anyhow!("Unknown/Complex type: {}", ty)),
|
||||
}
|
||||
}
|
||||
pub fn from_schema(
|
||||
schema: &Schema,
|
||||
openapi: &OpenApiSpec<Resolved>,
|
||||
|
88
src/proto/handle_services.rs
Normal file
88
src/proto/handle_services.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use convert_case::Case;
|
||||
use protox::prost_reflect::prost_types::{FileDescriptorSet, ServiceDescriptorProto};
|
||||
use tailcall_valid::{Valid, Validator};
|
||||
use crate::config::config::{Config, Function, Interface, Parameter, ReturnTy, UseStatement};
|
||||
use convert_case::Casing;
|
||||
use crate::proto::proto::process_ty;
|
||||
|
||||
fn handle_service(config: &Config, services: &[ServiceDescriptorProto]) -> Valid<Vec<Interface>, anyhow::Error, anyhow::Error> {
|
||||
Valid::from_iter(services.iter(), |service| {
|
||||
let name = service.name().to_case(Case::Kebab);
|
||||
Valid::from_iter(service.method.iter(), |method| {
|
||||
let name = method.name().to_case(Case::Kebab);
|
||||
let input_ty = if let Some(input) = method.input_type.as_ref() {
|
||||
process_ty(input).map(|v| Some(v))
|
||||
} else {
|
||||
Valid::succeed(None)
|
||||
};
|
||||
|
||||
let output_ty = if let Some(output) = method.output_type.as_ref() {
|
||||
process_ty(output).map(|v| Some(v))
|
||||
} else {
|
||||
Valid::succeed(None)
|
||||
};
|
||||
|
||||
input_ty.zip(output_ty).map(|(a, b)| {
|
||||
let mut parameters = vec![];
|
||||
let mut return_type = ReturnTy {
|
||||
return_type: "unit".to_string(),
|
||||
|
||||
// TODO: I am not yet sure about error type
|
||||
error_type: None,
|
||||
};
|
||||
if let Some(a) = a {
|
||||
// Protobuf only supports one input parameter,
|
||||
// so we can assume that the name is "input"
|
||||
parameters.push(Parameter {
|
||||
name: "input".to_string(),
|
||||
parameter_type: a.to_wit(None),
|
||||
});
|
||||
}
|
||||
if let Some(b) = b {
|
||||
return_type.return_type = b.to_wit(None);
|
||||
}
|
||||
Function {
|
||||
name,
|
||||
parameters,
|
||||
return_type,
|
||||
}
|
||||
})
|
||||
}).and_then(|functions| {
|
||||
Valid::from_iter(config.interfaces.iter(), |interface| {
|
||||
let use_name = interface.name.to_string();
|
||||
let mut imports = vec![];
|
||||
imports.extend(interface.records.iter().map(|v| v.name.to_string()));
|
||||
imports.extend(interface.varients.iter().map(|(k, _)| k.to_string()));
|
||||
|
||||
Valid::succeed(
|
||||
UseStatement {
|
||||
name: use_name,
|
||||
items: imports,
|
||||
}
|
||||
)
|
||||
}).and_then(|uses| {
|
||||
Valid::succeed(Interface {
|
||||
name,
|
||||
uses,
|
||||
functions,
|
||||
..Default::default()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_services(config: Config, proto: &[FileDescriptorSet]) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||
Valid::succeed(config)
|
||||
.and_then(|mut config| {
|
||||
Valid::from_iter(proto.iter(), |file| {
|
||||
Valid::from_iter(file.file.iter(), |file| {
|
||||
handle_service(&config, &file.service)
|
||||
})
|
||||
.and_then(|interfaces| {
|
||||
config.interfaces.extend(interfaces.into_iter().flatten().collect::<Vec<_>>());
|
||||
Valid::succeed(())
|
||||
})
|
||||
}).and_then(|_| Valid::succeed(config))
|
||||
})
|
||||
}
|
@ -1,24 +1,49 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use anyhow::anyhow;
|
||||
use convert_case::Case;
|
||||
|
||||
use protox::prost_reflect::prost_types::{EnumDescriptorProto, FileDescriptorProto, FileDescriptorSet};
|
||||
use protox::prost_reflect::prost_types::{DescriptorProto, EnumDescriptorProto, FileDescriptorProto, FileDescriptorSet};
|
||||
use tailcall_valid::{Valid, Validator};
|
||||
|
||||
use crate::config::config::{Config, Interface, Record};
|
||||
use crate::config::config::{Config, Field, Interface, Record};
|
||||
use crate::config::wit_types::WitType;
|
||||
use convert_case::Casing;
|
||||
use crate::proto::proto::process_ty;
|
||||
|
||||
fn append_enums(_: &Config, file: &[EnumDescriptorProto]) -> Valid<BTreeMap<String, WitType>, anyhow::Error, anyhow::Error> {
|
||||
fn append_enums(file: &[EnumDescriptorProto]) -> Valid<BTreeMap<String, WitType>, anyhow::Error, anyhow::Error> {
|
||||
Valid::from_iter(file.iter().enumerate(), |(i, enum_)| {
|
||||
let enum_name = enum_.name();
|
||||
let enum_name = enum_.name().to_case(Case::Kebab);
|
||||
Valid::from_iter(enum_.value.iter().enumerate(), |(j, value)| {
|
||||
Valid::succeed(value.name().to_string())
|
||||
Valid::succeed(value.name().to_case(Case::Kebab))
|
||||
}).and_then(|varients| {
|
||||
Valid::succeed((enum_name.to_string(), WitType::Enum(varients)))
|
||||
Valid::succeed((enum_name, WitType::Enum(varients)))
|
||||
})
|
||||
}).and_then(|rec| Valid::succeed(rec.into_iter().collect()))
|
||||
}
|
||||
|
||||
fn append_message(config: &Config, file: &FileDescriptorProto) -> Valid<Vec<Record>, anyhow::Error, anyhow::Error> {
|
||||
Valid::succeed(vec![])
|
||||
fn append_message(messages: &[DescriptorProto]) -> Valid<Vec<Record>, anyhow::Error, anyhow::Error> {
|
||||
Valid::from_iter(messages.iter(), |message| {
|
||||
let record_name = message.name().to_case(Case::Kebab);
|
||||
Valid::from_iter(message.field.iter().enumerate(), |(i, field)| {
|
||||
if let Some(ty_) = field.type_name.as_ref() {
|
||||
process_ty(ty_).map(|ty| (field.name().to_case(Case::Kebab), ty))
|
||||
} else {
|
||||
Valid::from(WitType::from_primitive_proto_type(field.r#type().as_str_name()))
|
||||
.map(|ty| (field.name().to_case(Case::Kebab), ty))
|
||||
}.and_then(|(name, ty)| {
|
||||
Valid::succeed(Field {
|
||||
name,
|
||||
field_type: ty,
|
||||
})
|
||||
})
|
||||
}).and_then(|fields| {
|
||||
Valid::succeed(Record {
|
||||
name: record_name,
|
||||
fields: fields.into_iter().collect(),
|
||||
..Default::default()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn handle_types(config: Config, proto: &[FileDescriptorSet], package: String) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||
@ -28,8 +53,8 @@ pub fn handle_types(config: Config, proto: &[FileDescriptorSet], package: String
|
||||
let mut map = BTreeMap::new();
|
||||
let mut records = BTreeSet::new();
|
||||
Valid::from_iter(set.file.iter(), |file| {
|
||||
append_enums(&config, &file.enum_type).and_then(|varients| {
|
||||
append_message(&config, file)
|
||||
append_enums(&file.enum_type).and_then(|varients| {
|
||||
append_message(&file.message_type)
|
||||
.and_then(|recs| {
|
||||
map.extend(varients);
|
||||
records.extend(BTreeSet::from_iter(recs.into_iter()));
|
||||
|
@ -1,2 +1,3 @@
|
||||
mod proto;
|
||||
mod handle_types;
|
||||
mod handle_types;
|
||||
mod handle_services;
|
@ -1,6 +1,9 @@
|
||||
use anyhow::anyhow;
|
||||
use protox::prost_reflect::prost_types::FileDescriptorSet;
|
||||
use tailcall_valid::{Valid, Validator};
|
||||
use crate::config::config::Config;
|
||||
use crate::config::wit_types::WitType;
|
||||
use crate::proto::handle_services::handle_services;
|
||||
use crate::proto::handle_types::handle_types;
|
||||
|
||||
pub struct Proto(Vec<FileDescriptorSet>);
|
||||
@ -12,6 +15,19 @@ impl Proto {
|
||||
pub fn to_config(&self) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||
Valid::succeed(Config::default())
|
||||
.and_then(|config| handle_types(config, &self.0, "api:todos@1.0.0".to_string()))
|
||||
.and_then(|config| handle_services(config, &self.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_ty(name: &str) -> Valid<WitType, anyhow::Error, anyhow::Error> {
|
||||
if !name.starts_with('.') {
|
||||
return Valid::fail(anyhow!("Expected fully-qualified name for reference type but got {name}. This is a bug!"));
|
||||
}
|
||||
let name = &name[1..];
|
||||
if let Some((_package, name)) = name.rsplit_once('.') {
|
||||
Valid::succeed(WitType::FieldTy(name.to_string()))
|
||||
}else {
|
||||
Valid::succeed(WitType::FieldTy(name.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user