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;}
|
message TodoListRequest {string user_id = 1;}
|
||||||
|
|
||||||
enum Error {
|
|
||||||
UNKNOWN = 0;
|
|
||||||
NOT_FOUND = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TodoAddRequest {
|
message TodoAddRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
string task = 2;
|
string task = 2;
|
||||||
|
@ -74,7 +74,7 @@ pub struct Function {
|
|||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct ReturnTy {
|
pub struct ReturnTy {
|
||||||
pub return_type: String,
|
pub return_type: String,
|
||||||
pub error_type: String,
|
pub error_type: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
|
@ -170,7 +170,7 @@ impl Function {
|
|||||||
};
|
};
|
||||||
|
|
||||||
format!(
|
format!(
|
||||||
"func {}({}){}",
|
"{}: func({}){};",
|
||||||
generate_wit_name(&self.name),
|
generate_wit_name(&self.name),
|
||||||
params,
|
params,
|
||||||
return_type
|
return_type
|
||||||
@ -180,14 +180,10 @@ impl Function {
|
|||||||
|
|
||||||
impl ReturnTy {
|
impl ReturnTy {
|
||||||
pub fn to_wit(&self) -> String {
|
pub fn to_wit(&self) -> String {
|
||||||
if self.error_type.is_empty() {
|
if let Some(err) = self.error_type.as_ref() {
|
||||||
self.return_type.clone()
|
format!("result<{}, {}>", self.return_type, err)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!("option<{}>", self.return_type)
|
||||||
"result<{}, {}>",
|
|
||||||
self.return_type,
|
|
||||||
self.error_type
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,21 @@ pub enum WitType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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(
|
pub fn from_schema(
|
||||||
schema: &Schema,
|
schema: &Schema,
|
||||||
openapi: &OpenApiSpec<Resolved>,
|
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 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 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 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_)| {
|
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::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| {
|
}).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()))
|
}).and_then(|rec| Valid::succeed(rec.into_iter().collect()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_message(config: &Config, file: &FileDescriptorProto) -> Valid<Vec<Record>, anyhow::Error, anyhow::Error> {
|
fn append_message(messages: &[DescriptorProto]) -> Valid<Vec<Record>, anyhow::Error, anyhow::Error> {
|
||||||
Valid::succeed(vec![])
|
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> {
|
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 map = BTreeMap::new();
|
||||||
let mut records = BTreeSet::new();
|
let mut records = BTreeSet::new();
|
||||||
Valid::from_iter(set.file.iter(), |file| {
|
Valid::from_iter(set.file.iter(), |file| {
|
||||||
append_enums(&config, &file.enum_type).and_then(|varients| {
|
append_enums(&file.enum_type).and_then(|varients| {
|
||||||
append_message(&config, file)
|
append_message(&file.message_type)
|
||||||
.and_then(|recs| {
|
.and_then(|recs| {
|
||||||
map.extend(varients);
|
map.extend(varients);
|
||||||
records.extend(BTreeSet::from_iter(recs.into_iter()));
|
records.extend(BTreeSet::from_iter(recs.into_iter()));
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
mod proto;
|
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 protox::prost_reflect::prost_types::FileDescriptorSet;
|
||||||
use tailcall_valid::{Valid, Validator};
|
use tailcall_valid::{Valid, Validator};
|
||||||
use crate::config::config::Config;
|
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;
|
use crate::proto::handle_types::handle_types;
|
||||||
|
|
||||||
pub struct Proto(Vec<FileDescriptorSet>);
|
pub struct Proto(Vec<FileDescriptorSet>);
|
||||||
@ -12,6 +15,19 @@ impl Proto {
|
|||||||
pub fn to_config(&self) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
pub fn to_config(&self) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||||
Valid::succeed(Config::default())
|
Valid::succeed(Config::default())
|
||||||
.and_then(|config| handle_types(config, &self.0, "api:todos@1.0.0".to_string()))
|
.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