partial
This commit is contained in:
parent
fe3ec74e35
commit
5eeb1b6002
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -23,6 +23,12 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "beef"
|
name = "beef"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -176,6 +182,7 @@ name = "foo"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"base64",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"oapi",
|
"oapi",
|
||||||
@ -188,6 +195,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml 0.9.34+deprecated",
|
"serde_yaml 0.9.34+deprecated",
|
||||||
"sppparse",
|
"sppparse",
|
||||||
|
"strum_macros",
|
||||||
"tailcall-valid",
|
"tailcall-valid",
|
||||||
"wit-parser",
|
"wit-parser",
|
||||||
]
|
]
|
||||||
@ -236,6 +244,12 @@ version = "0.15.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -875,6 +889,12 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -1075,6 +1095,19 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.90",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -19,4 +19,5 @@ serde = { version = "1.0.216", features = ["derive"] }
|
|||||||
tailcall-valid = "0.1.3"
|
tailcall-valid = "0.1.3"
|
||||||
wit-parser = "0.222.0"
|
wit-parser = "0.222.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
strum_macros = "0.26.4"
|
||||||
|
95
openapi.yaml
95
openapi.yaml
@ -88,25 +88,25 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
type: string
|
type: array
|
||||||
# items:
|
items:
|
||||||
# $ref: '#/components/schemas/Todo'
|
$ref: '#/components/schemas/Todo'
|
||||||
metadata:
|
metadata:
|
||||||
type: string
|
type: object
|
||||||
# properties:
|
properties:
|
||||||
# total:
|
total:
|
||||||
# type: integer
|
type: integer
|
||||||
# minimum: 0
|
minimum: 0
|
||||||
# limit:
|
limit:
|
||||||
# type: integer
|
type: integer
|
||||||
# minimum: 1
|
minimum: 1
|
||||||
# offset:
|
offset:
|
||||||
# type: integer
|
type: integer
|
||||||
# minimum: 0
|
minimum: 0
|
||||||
# required:
|
required:
|
||||||
# - total
|
- total
|
||||||
# - limit
|
- limit
|
||||||
# - offset
|
- offset
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
- metadata
|
- metadata
|
||||||
@ -115,8 +115,7 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
type: string
|
$ref: '#/components/schemas/Todo'
|
||||||
# $ref: '#/components/schemas/Todo'
|
|
||||||
required:
|
required:
|
||||||
- data
|
- data
|
||||||
|
|
||||||
@ -124,34 +123,34 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
error:
|
error:
|
||||||
type: string
|
type: object
|
||||||
# properties:
|
properties:
|
||||||
# code:
|
code:
|
||||||
# type: string
|
type: string
|
||||||
# enum:
|
enum:
|
||||||
# - VALIDATION_ERROR
|
- VALIDATION_ERROR
|
||||||
# - UNAUTHORIZED
|
- UNAUTHORIZED
|
||||||
# - FORBIDDEN
|
- FORBIDDEN
|
||||||
# - NOT_FOUND
|
- NOT_FOUND
|
||||||
# - RATE_LIMIT_EXCEEDED
|
- RATE_LIMIT_EXCEEDED
|
||||||
# - INTERNAL_ERROR
|
- INTERNAL_ERROR
|
||||||
# message:
|
message:
|
||||||
# type: string
|
type: string
|
||||||
# details:
|
details:
|
||||||
# type: array
|
type: array
|
||||||
# items:
|
items:
|
||||||
# type: object
|
type: object
|
||||||
# properties:
|
properties:
|
||||||
# field:
|
field:
|
||||||
# type: string
|
type: string
|
||||||
# message:
|
message:
|
||||||
# type: string
|
type: string
|
||||||
# required:
|
required:
|
||||||
# - field
|
- field
|
||||||
# - message
|
- message
|
||||||
# required:
|
required:
|
||||||
# - code
|
- code
|
||||||
# - message
|
- message
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
TodoId:
|
TodoId:
|
||||||
|
@ -27,6 +27,7 @@ pub struct Interface {
|
|||||||
pub struct Record {
|
pub struct Record {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub fields: Vec<Field>,
|
pub fields: Vec<Field>,
|
||||||
|
pub added_fields: Vec<Field>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct UseStatement {
|
pub struct UseStatement {
|
||||||
|
143
src/config/fixargs.rs
Normal file
143
src/config/fixargs.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use base64::Engine;
|
||||||
|
use tailcall_valid::{Valid, Validator};
|
||||||
|
use crate::config::config::{Config, Field, Interface, Record};
|
||||||
|
use crate::config::wit_types::WitType;
|
||||||
|
use crate::tryfold::TryFold;
|
||||||
|
|
||||||
|
pub fn fix_args(config: Config) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||||
|
Valid::succeed(config)
|
||||||
|
.and_then(|mut config| {
|
||||||
|
Valid::from_iter(config.interfaces.iter(), |interface| {
|
||||||
|
Valid::from_iter(interface.records.iter(), |record| {
|
||||||
|
Valid::from_iter(record.fields.iter(), |field| {
|
||||||
|
if !field.field_type.is_kind_of_primitive() {
|
||||||
|
fix_field()
|
||||||
|
.try_fold(&(&config, interface, record, &field, &field.field_type), interface.clone())
|
||||||
|
.and_then(|new_interface| {
|
||||||
|
Valid::succeed(new_interface)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Valid::succeed(interface.clone())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).and_then(|x| {
|
||||||
|
let x = x.into_iter().flatten().into_iter().flatten().collect::<Vec<_>>();
|
||||||
|
config.interfaces = x;
|
||||||
|
|
||||||
|
Valid::succeed(config)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_field<'a>() -> TryFold<'a, (&'a Config, &'a Interface, &'a Record, &'a Field, &'a WitType), Interface, anyhow::Error> {
|
||||||
|
TryFold::<(&Config, &Interface, &Record, &Field, &WitType), Interface, anyhow::Error>::new(move |(config, interface, rec, field, wit), mut o| {
|
||||||
|
match &wit {
|
||||||
|
WitType::Option(x) => {
|
||||||
|
return fix_field().try_fold(&(*config, *interface, *rec, *field, x), o);
|
||||||
|
}
|
||||||
|
WitType::Result(a, b) => {
|
||||||
|
// TODO: this impl needs check.
|
||||||
|
// eg: -> result<record A {}, record B {}>
|
||||||
|
// this does not make sense to resolve with rec.name-field.name-A/B
|
||||||
|
|
||||||
|
return if !a.is_primitive() {
|
||||||
|
fix_field().try_fold(&(*config, *interface, *rec, *field, a), o)
|
||||||
|
} else if !b.is_primitive() {
|
||||||
|
fix_field().try_fold(&(*config, *interface, *rec, *field, b), o)
|
||||||
|
} else {
|
||||||
|
// TODO: Fix the possible conflicts here
|
||||||
|
fix_field().try_fold(&(*config, *interface, *rec, *field, a), o)
|
||||||
|
.and_then(|new_interface| fix_field().try_fold(&(*config, *interface, *rec, *field, b), new_interface))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
WitType::List(x) => fix_field().try_fold(&(*config, *interface, *rec, *field, x.as_ref()), o),
|
||||||
|
WitType::Tuple(x) => {
|
||||||
|
// TODO: Fix the possible conflicts here
|
||||||
|
return Valid::from_iter(x.iter(), |x| fix_field().try_fold(&(*config, *interface, *rec, *field, x), o.clone()))
|
||||||
|
.and_then(|v| Valid::succeed(v.into_iter().fold((*interface).clone(), |mut a, b| {
|
||||||
|
a.records.extend(b.records);
|
||||||
|
a
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
WitType::Record(records) => {
|
||||||
|
for (arg_name, ty) in records {
|
||||||
|
let arg_name = if arg_name.is_empty() {
|
||||||
|
// TODO: maybe we can introduce some transformers
|
||||||
|
// which uses AI to generate better names
|
||||||
|
base64::prelude::BASE64_STANDARD.encode(arg_name.as_bytes())
|
||||||
|
} else {
|
||||||
|
arg_name.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = format!("{}-{}-{}", rec.name, field.name, arg_name);
|
||||||
|
let new_field = Field {
|
||||||
|
name,
|
||||||
|
field_type: ty.clone(),
|
||||||
|
};
|
||||||
|
let mut rec = (*rec).clone();
|
||||||
|
rec.added_fields.push(new_field);
|
||||||
|
let mut filtered_records = o.records.into_iter().filter(|r| r.name == rec.name).collect::<Vec<_>>();
|
||||||
|
filtered_records.push(rec);
|
||||||
|
|
||||||
|
o.records = filtered_records;
|
||||||
|
}
|
||||||
|
Valid::succeed(o)
|
||||||
|
|
||||||
|
/* Valid::from_iter(records.iter(), |(_, b)| {
|
||||||
|
fix_field()
|
||||||
|
.try_fold(&(*config, *interface, *rec, *field, b), o.clone())
|
||||||
|
}).and_then(|v| {
|
||||||
|
o.records = v;
|
||||||
|
Valid::succeed(o)
|
||||||
|
}).and_then(|mut o| {
|
||||||
|
|
||||||
|
})*/
|
||||||
|
}
|
||||||
|
// Ideally this case should never happen,
|
||||||
|
// but we should always throw an error to avoid infinite recursion
|
||||||
|
_ => return Valid::fail(anyhow::anyhow!("Unknown type: {:?}", field.field_type)),
|
||||||
|
}
|
||||||
|
// Valid::succeed(o)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*fn fix_field1(ty: &WitType, mut record: Record, interface: Interface) -> Valid<Record, anyhow::Error, anyhow::Error> {
|
||||||
|
match ty {
|
||||||
|
WitType::Option(x) => {
|
||||||
|
fix_field1(x, record)
|
||||||
|
}
|
||||||
|
WitType::Result(x, y) => {
|
||||||
|
if !x.is_primitive() && !y.is_primitive() {
|
||||||
|
fix_field1(x, record).and_then(|x| fix_field1(y, x))
|
||||||
|
} else if !x.is_primitive() {
|
||||||
|
fix_field1(x, record)
|
||||||
|
} else {
|
||||||
|
fix_field1(y, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WitType::List(x) => {
|
||||||
|
fix_field1(x, record)
|
||||||
|
}
|
||||||
|
WitType::Tuple(x) => {
|
||||||
|
Valid::from_iter(x.iter(), |x| fix_field1(x, record))
|
||||||
|
.and_then(|v| Valid::succeed(v.into_iter().fold(Record::default(), |mut a, b| {
|
||||||
|
a.added_fields.extend(b.added_fields);
|
||||||
|
a
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
WitType::Record(val) => {
|
||||||
|
let mut new_field = Field::default();
|
||||||
|
for (k, v) in val {
|
||||||
|
new_field.field_type = v.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
record.added_fields.push(new_field);
|
||||||
|
Valid::succeed(record)
|
||||||
|
}
|
||||||
|
// Ideally this case should never happen
|
||||||
|
// but we should always throw an error to avoid infinite recursion
|
||||||
|
_ => Valid::fail(anyhow::anyhow!("Unknown type: {:?}", ty)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
48
src/config/handle_files.rs
Normal file
48
src/config/handle_files.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use tailcall_valid::{Valid, Validator};
|
||||||
|
use crate::config::config::{Config, Field, Interface, Record};
|
||||||
|
use crate::config::wit_types::WitType;
|
||||||
|
use crate::ser::OpenApiSpec;
|
||||||
|
|
||||||
|
pub fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid<Config, Error, Error> {
|
||||||
|
Valid::succeed(config).and_then(|mut config: Config| {
|
||||||
|
Valid::from_option(spec.components.as_ref(), anyhow!("Components are required"))
|
||||||
|
.and_then(|v| Valid::from_option(v.schemas.as_ref(), anyhow!("Schemas are required")))
|
||||||
|
.and_then(|schemas| {
|
||||||
|
Valid::from_iter(schemas.iter(), |(record_name, v)| {
|
||||||
|
Valid::from_option(v.type_.as_ref(), anyhow!("Type is required"))
|
||||||
|
.and_then(|type_| {
|
||||||
|
Valid::from_option(v.properties.as_ref(), anyhow!("Properties are required"))
|
||||||
|
.and_then(|a| {
|
||||||
|
Valid::from_iter(a, |(field_name, v)| {
|
||||||
|
Valid::from(WitType::from_schema(v, spec))
|
||||||
|
.and_then(|wit_type| {
|
||||||
|
let field = Field {
|
||||||
|
name: field_name.clone(),
|
||||||
|
field_type: wit_type,
|
||||||
|
};
|
||||||
|
Valid::succeed(field)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.and_then(|fields| {
|
||||||
|
let record = Record {
|
||||||
|
name: record_name.clone(),
|
||||||
|
fields,
|
||||||
|
added_fields: vec![],
|
||||||
|
};
|
||||||
|
Valid::succeed(record)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).and_then(|records| {
|
||||||
|
let interface = Interface {
|
||||||
|
name: "types".to_string(),
|
||||||
|
records,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
config.interfaces.push(interface);
|
||||||
|
Valid::succeed(())
|
||||||
|
})
|
||||||
|
}).and_then(|_| Valid::succeed(config))
|
||||||
|
})
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod wit_types;
|
pub mod wit_types;
|
||||||
pub mod to_wit;
|
pub mod to_wit;
|
||||||
|
pub mod fixargs;
|
||||||
|
pub mod handle_files;
|
||||||
|
@ -239,7 +239,7 @@ impl ToWit for WitType {
|
|||||||
"{{ {} }}",
|
"{{ {} }}",
|
||||||
fields
|
fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, ty)| format!("{}: {}", generate_wit_name(name), ty))
|
.map(|(name, ty)| format!("{}: {}", generate_wit_name(name), ty.to_wit()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,7 @@ use crate::config::to_wit::ToWit;
|
|||||||
|
|
||||||
use crate::ser::{OpenApiSpec, Schema};
|
use crate::ser::{OpenApiSpec, Schema};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, strum_macros::Display)]
|
||||||
pub enum WitType {
|
pub enum WitType {
|
||||||
// Primitive Types
|
// Primitive Types
|
||||||
Bool,
|
Bool,
|
||||||
@ -28,7 +28,7 @@ pub enum WitType {
|
|||||||
Tuple(Vec<WitType>), // (T1, T2, ...)
|
Tuple(Vec<WitType>), // (T1, T2, ...)
|
||||||
|
|
||||||
// Custom Types
|
// Custom Types
|
||||||
Record(Vec<(String, String)>), // Record { field_name: Type }
|
Record(Vec<(String, WitType)>), // Record { field_name: Type }
|
||||||
Variant(Vec<(String, Option<WitType>)>), // Variant { name: Option<Type> }
|
Variant(Vec<(String, Option<WitType>)>), // Variant { name: Option<Type> }
|
||||||
Enum(Vec<String>), // Enum { name1, name2, ... }
|
Enum(Vec<String>), // Enum { name1, name2, ... }
|
||||||
Flags(Vec<String>), // Flags { flag1, flag2, ... }
|
Flags(Vec<String>), // Flags { flag1, flag2, ... }
|
||||||
@ -39,6 +39,26 @@ pub enum WitType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WitType {
|
impl WitType {
|
||||||
|
pub fn is_primitive(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
WitType::Bool | WitType::U8 | WitType::U16 | WitType::U32 | WitType::U64 | WitType::S8 | WitType::S16 | WitType::S32 | WitType::S64 | WitType::Float32 | WitType::Float64 | WitType::Char | WitType::String => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_kind_of_primitive(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
WitType::Option(x) => x.is_primitive(),
|
||||||
|
WitType::Result(a, b) => a.is_primitive() && b.is_primitive(),
|
||||||
|
WitType::List(x) => x.is_primitive(),
|
||||||
|
WitType::Tuple(x) => x.iter().all(|x| x.is_primitive()),
|
||||||
|
WitType::Variant(_) => true,
|
||||||
|
WitType::Enum(_) => true,
|
||||||
|
WitType::Flags(_) => true,
|
||||||
|
WitType::Handle(_) => true,
|
||||||
|
WitType::TypeAlias(_, _) => true,
|
||||||
|
v => v.is_primitive(),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn from_schema(
|
pub fn from_schema(
|
||||||
schema: &Schema,
|
schema: &Schema,
|
||||||
openapi: &OpenApiSpec,
|
openapi: &OpenApiSpec,
|
||||||
@ -66,7 +86,7 @@ impl WitType {
|
|||||||
Valid::from_option(schema.properties.as_ref(), anyhow::anyhow!("Properties are required"))
|
Valid::from_option(schema.properties.as_ref(), anyhow::anyhow!("Properties are required"))
|
||||||
.and_then(|properties| {
|
.and_then(|properties| {
|
||||||
Valid::from_iter(properties.iter(), |(name, schema)| {
|
Valid::from_iter(properties.iter(), |(name, schema)| {
|
||||||
Valid::from(WitType::from_schema(schema, openapi)).map(|ty| (name.clone(), ty.to_wit()))
|
Valid::from(WitType::from_schema(schema, openapi)).map(|ty| (name.clone(), ty))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -175,8 +195,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
WitType::Record(vec![
|
WitType::Record(vec![
|
||||||
("id".to_string(), WitType::S32.to_wit()),
|
("id".to_string(), WitType::S32),
|
||||||
("name".to_string(), WitType::String.to_wit()),
|
("name".to_string(), WitType::String),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ mod value;
|
|||||||
mod proto;
|
mod proto;
|
||||||
mod ser;
|
mod ser;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod transformer;
|
||||||
|
mod transform;
|
||||||
|
mod tryfold;
|
||||||
|
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
59
src/proto.rs
59
src/proto.rs
@ -1,65 +1,24 @@
|
|||||||
use anyhow::{anyhow, Error};
|
use anyhow::Error;
|
||||||
use tailcall_valid::{Valid, Validator};
|
use tailcall_valid::{Valid, Validator};
|
||||||
|
|
||||||
use crate::config::config::{Config, Field, Interface, Record};
|
use crate::config::config::Config;
|
||||||
use crate::config::wit_types::WitType;
|
use crate::config::fixargs::fix_args;
|
||||||
|
use crate::config::handle_files::handle_types;
|
||||||
use crate::ser::OpenApiSpec;
|
use crate::ser::OpenApiSpec;
|
||||||
|
|
||||||
fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid<Config, Error, Error> {
|
|
||||||
Valid::succeed(config).and_then(|mut config: Config| {
|
|
||||||
Valid::from_option(spec.components.as_ref(), anyhow!("Components are required"))
|
|
||||||
.and_then(|v| Valid::from_option(v.schemas.as_ref(), anyhow!("Schemas are required")))
|
|
||||||
.and_then(|schemas| {
|
|
||||||
Valid::from_iter(schemas.iter(), |(record_name, v)| {
|
|
||||||
Valid::from_option(v.type_.as_ref(), anyhow!("Type is required"))
|
|
||||||
.and_then(|type_| {
|
|
||||||
Valid::from_option(v.properties.as_ref(), anyhow!("Properties are required"))
|
|
||||||
.and_then(|a| {
|
|
||||||
Valid::from_iter(a, |(field_name, v)| {
|
|
||||||
Valid::from(WitType::from_schema(v, spec))
|
|
||||||
.and_then(|wit_type| {
|
|
||||||
let field = Field {
|
|
||||||
name: field_name.clone(),
|
|
||||||
field_type: wit_type,
|
|
||||||
};
|
|
||||||
Valid::succeed(field)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.and_then(|fields| {
|
|
||||||
let record = Record {
|
|
||||||
name: record_name.clone(),
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
Valid::succeed(record)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}).and_then(|records| {
|
|
||||||
let interface = Interface {
|
|
||||||
name: "types".to_string(),
|
|
||||||
records,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
config.interfaces.push(interface);
|
|
||||||
Valid::succeed(())
|
|
||||||
})
|
|
||||||
}).and_then(|_| Valid::succeed(config))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_config(spec: OpenApiSpec) -> Valid<Config, Error, Error> {
|
fn to_config(spec: OpenApiSpec) -> Valid<Config, Error, Error> {
|
||||||
Valid::succeed(Config::default())
|
Valid::succeed(Config::default())
|
||||||
.and_then(|config| handle_types(config, &spec))
|
.and_then(|config| handle_types(config, &spec))
|
||||||
|
.and_then(|config| fix_args(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod t {
|
mod t {
|
||||||
use tailcall_valid::Validator;
|
use tailcall_valid::Validator;
|
||||||
use wit_parser::Resolve;
|
|
||||||
|
|
||||||
|
use crate::config::to_wit::ToWit;
|
||||||
use crate::proto::to_config;
|
use crate::proto::to_config;
|
||||||
use crate::ser::OpenApiSpec;
|
use crate::ser::OpenApiSpec;
|
||||||
use crate::config::to_wit::ToWit;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tp() {
|
fn tp() {
|
||||||
@ -72,9 +31,9 @@ mod t {
|
|||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
v.package = "api:todos@1.0.0".to_string();
|
v.package = "api:todos@1.0.0".to_string();
|
||||||
println!("{}", v.to_wit());
|
println!("{}", v.to_wit());
|
||||||
let mut resolve = Resolve::new();
|
// let mut resolve = Resolve::new();
|
||||||
resolve.push_str("foox.wit", &v.to_wit()).expect("TODO: panic message`");
|
// resolve.push_str("foox.wit", &v.to_wit()).expect("TODO: panic message`");
|
||||||
println!("{:#?}", resolve);
|
// println!("{:#?}", resolve);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("err: {:?}", e);
|
println!("err: {:?}", e);
|
||||||
|
0
src/transform/mod.rs
Normal file
0
src/transform/mod.rs
Normal file
7
src/transformer.rs
Normal file
7
src/transformer.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use tailcall_valid::Valid;
|
||||||
|
|
||||||
|
pub trait Transform {
|
||||||
|
type Value;
|
||||||
|
type Error;
|
||||||
|
fn transform(&self, value: Self::Value) -> Valid<Self::Value, Self::Error, Self::Error>;
|
||||||
|
}
|
302
src/tryfold.rs
Normal file
302
src/tryfold.rs
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
use tailcall_valid::{Valid, Validator};
|
||||||
|
|
||||||
|
/// Trait for types that support a "try fold" operation.
|
||||||
|
///
|
||||||
|
/// `TryFolding` describes a composable folding operation that can potentially
|
||||||
|
/// fail. It can optionally consume an input to transform the provided value.
|
||||||
|
type TryFoldFn<'a, I, O, E> = Box<dyn Fn(&I, O) -> Valid<O, E, E> + 'a>;
|
||||||
|
|
||||||
|
pub struct TryFold<'a, I: 'a, O: 'a, E: 'a>(TryFoldFn<'a, I, O, E>);
|
||||||
|
|
||||||
|
impl<'a, I, O: Clone + 'a, E> TryFold<'a, I, O, E> {
|
||||||
|
/// Try to fold the value with the input.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `input`: The input used in the folding operation.
|
||||||
|
/// - `value`: The value to be folded.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a `Valid` value, which can be either a success with the folded
|
||||||
|
/// value or an error.
|
||||||
|
pub fn try_fold(&self, input: &I, state: O) -> Valid<O, E, E> {
|
||||||
|
(self.0)(input, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combine two `TryFolding` implementors into a sequential operation.
|
||||||
|
///
|
||||||
|
/// This method allows for chaining two `TryFolding` operations, where the
|
||||||
|
/// result of the first operation (if successful) will be used as the
|
||||||
|
/// input for the second operation.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `other`: Another `TryFolding` implementor.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a combined `And` structure that represents the sequential
|
||||||
|
/// folding operation.
|
||||||
|
pub fn and(self, other: TryFold<'a, I, O, E>) -> Self {
|
||||||
|
TryFold(Box::new(move |input, state| {
|
||||||
|
self.try_fold(input, state.clone()).fold(
|
||||||
|
|state| other.try_fold(input, state),
|
||||||
|
|| other.try_fold(input, state),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `TryFold` with a specified folding function.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `f`: The folding function.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a new `TryFold` instance.
|
||||||
|
pub fn new(f: impl Fn(&I, O) -> Valid<O, E, E> + 'a) -> Self {
|
||||||
|
TryFold(Box::new(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms a TryFold<I, O, E> to TryFold<I, O1, E> by applying
|
||||||
|
/// transformations. Check `transform_valid` if you want to return a
|
||||||
|
/// `Valid` instead of an `O1`.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `up`: A function that uses O and O1 to create a new O1.
|
||||||
|
/// - `down`: A function that uses O1 to create a new O.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a new TryFold<I, O1, E> that applies the transformations.
|
||||||
|
pub fn transform<O1: Clone>(
|
||||||
|
self,
|
||||||
|
up: impl Fn(O, O1) -> O1 + 'a,
|
||||||
|
down: impl Fn(O1) -> O + 'a,
|
||||||
|
) -> TryFold<'a, I, O1, E> {
|
||||||
|
self.transform_valid(
|
||||||
|
move |o, o1| Valid::succeed(up(o, o1)),
|
||||||
|
move |o1| Valid::succeed(down(o1)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms a TryFold<I, O, E> to TryFold<I, O1, E> by applying
|
||||||
|
/// transformations. Check `transform` if you want to return an `O1`
|
||||||
|
/// instead of a `Valid`.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `up`: A function that uses O and O1 to create a new Valid<O1, E>.
|
||||||
|
/// - `down`: A function that uses O1 to create a new Valid<O, E>.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a new TryFold<I, O1, E> that applies the transformations.
|
||||||
|
pub fn transform_valid<O1: Clone>(
|
||||||
|
self,
|
||||||
|
up: impl Fn(O, O1) -> Valid<O1, E, E> + 'a,
|
||||||
|
down: impl Fn(O1) -> Valid<O, E, E> + 'a,
|
||||||
|
) -> TryFold<'a, I, O1, E> {
|
||||||
|
TryFold(Box::new(move |i, o1| {
|
||||||
|
down(o1.clone())
|
||||||
|
.and_then(|o| self.try_fold(i, o))
|
||||||
|
.and_then(|o| up(o, o1))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(self, f: impl Fn(O) -> O + 'a) -> TryFold<'a, I, O, E> {
|
||||||
|
self.transform(move |o, _| f(o), |o| o)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `TryFold` that doesn't do anything.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a `TryFold` that doesn't do anything.
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
TryFold::new(|_, o| Valid::succeed(o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I, O: Clone, E> FromIterator<TryFold<'a, I, O, E>> for TryFold<'a, I, O, E> {
|
||||||
|
fn from_iter<T: IntoIterator<Item = TryFold<'a, I, O, E>>>(iter: T) -> Self {
|
||||||
|
let mut iter = iter.into_iter();
|
||||||
|
let head = iter.next();
|
||||||
|
|
||||||
|
if let Some(head) = head {
|
||||||
|
head.and(TryFold::from_iter(iter))
|
||||||
|
} else {
|
||||||
|
TryFold::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use tailcall_valid::{Cause, Valid, Validator};
|
||||||
|
|
||||||
|
use super::TryFold;
|
||||||
|
|
||||||
|
impl<'a, I, O: Clone + 'a, E> TryFold<'a, I, O, E> {
|
||||||
|
/// Create a `TryFold` that always succeeds with the provided state.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `state`: The state to succeed with.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// Returns a `TryFold` that always succeeds with the provided state.
|
||||||
|
pub fn succeed(f: impl Fn(&I, O) -> O + 'a) -> Self {
|
||||||
|
TryFold(Box::new(move |i, o| Valid::succeed(f(i, o))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_and() {
|
||||||
|
let t1 = TryFold::<i32, i32, ()>::new(|a: &i32, b: i32| Valid::succeed(a + b));
|
||||||
|
let t2 = TryFold::<i32, i32, ()>::new(|a: &i32, b: i32| Valid::succeed(a * b));
|
||||||
|
let t = t1.and(t2);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap();
|
||||||
|
let expected = 10;
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_one_failure() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::fail(a + b));
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a * b));
|
||||||
|
let t = t1.and(t2);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = vec![Cause::new(5)];
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #[test]
|
||||||
|
fn test_both_failure() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::fail(a + b));
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b));
|
||||||
|
let t = t1.and(t2);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(5).combine(ValidationError::new(6));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#[test]
|
||||||
|
fn test_order() {
|
||||||
|
let calls = RefCell::new(Vec::new());
|
||||||
|
let t1 = TryFold::<i32, i32, ()>::new(|a: &i32, b: i32| {
|
||||||
|
calls.borrow_mut().push(1);
|
||||||
|
Valid::succeed(a + b)
|
||||||
|
}); // 2 + 3
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| {
|
||||||
|
calls.borrow_mut().push(2);
|
||||||
|
Valid::succeed(a * b)
|
||||||
|
}); // 2 * 3
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| {
|
||||||
|
calls.borrow_mut().push(3);
|
||||||
|
Valid::succeed(a * b * 100)
|
||||||
|
}); // 2 * 6
|
||||||
|
let _t = t1.and(t2).and(t3).try_fold(&2, 3);
|
||||||
|
|
||||||
|
assert_eq!(*calls.borrow(), vec![1, 2, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #[test]
|
||||||
|
fn test_1_3_failure_left() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::fail(a + b)); // 2 + 3
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a * b)); // 2 * 3
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b * 100)); // 2 * 6
|
||||||
|
let t = t1.and(t2).and(t3);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(5).combine(ValidationError::new(600));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_1_3_failure_right() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::fail(a + b)); // 2 + 3
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a * b)); // 2 * 3
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b * 100)); // 2 * 6
|
||||||
|
let t = t1.and(t2.and(t3));
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(5).combine(ValidationError::new(1200));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_2_3_failure() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a + b));
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b));
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b * 100));
|
||||||
|
let t = t1.and(t2.and(t3));
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(10).combine(ValidationError::new(1000));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_all() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a + b));
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b));
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b * 100));
|
||||||
|
let t = TryFold::from_iter(vec![t1, t2, t3]);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(10).combine(ValidationError::new(1000));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_all_1_3_fail() {
|
||||||
|
let t1 = TryFold::new(|a: &i32, b: i32| Valid::fail(a + b));
|
||||||
|
let t2 = TryFold::new(|a: &i32, b: i32| Valid::succeed(a * b));
|
||||||
|
let t3 = TryFold::new(|a: &i32, b: i32| Valid::fail(a * b * 100));
|
||||||
|
let t = TryFold::from_iter(vec![t1, t2, t3]);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap_err();
|
||||||
|
let expected = ValidationError::new(5).combine(ValidationError::new(1200));
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform() {
|
||||||
|
let t: TryFold<'_, i32, String, ()> = TryFold::succeed(|a: &i32, b: i32| a + b).transform(
|
||||||
|
|v: i32, _| v.to_string(),
|
||||||
|
|v: String| v.parse::<i32>().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, "3".to_string()).to_result().unwrap();
|
||||||
|
let expected = "5".to_string();
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_valid() {
|
||||||
|
let t: TryFold<'_, i32, String, ()> = TryFold::succeed(|a: &i32, b: i32| a + b)
|
||||||
|
.transform_valid(
|
||||||
|
|v: i32, _| Valid::succeed(v.to_string()),
|
||||||
|
|v: String| Valid::succeed(v.parse::<i32>().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = t.try_fold(&2, "3".to_string()).to_result().unwrap();
|
||||||
|
let expected = "5".to_string();
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update() {
|
||||||
|
let t = TryFold::<i32, i32, String>::succeed(|a: &i32, b: i32| a + b).update(|a| a + 1);
|
||||||
|
let actual = t.try_fold(&2, 3).to_result().unwrap();
|
||||||
|
let expected = 6;
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user