use syn::{PathSegment, Type};
#[derive(Clone, Debug)]
pub enum RustType {
U8,
I8,
U16,
I16,
U32,
I32,
U64,
I64,
U128,
I128,
USize,
ISize,
F32,
F64,
Bool,
String,
Option(Box<RustType>),
Vec(Box<RustType>),
TypedArray(Box<RustType>),
Tuple(Vec<RustType>),
HashMap(Box<RustType>, Box<RustType>),
User(String),
}
impl RustType {
pub fn ts_type(&self) -> String {
match self {
RustType::U8 => "number".to_string(),
RustType::I8 => "number".to_string(),
RustType::U16 => "number".to_string(),
RustType::I16 => "number".to_string(),
RustType::U32 => "number".to_string(),
RustType::I32 => "number".to_string(),
RustType::U64 => "bigint".to_string(),
RustType::I64 => "bigint".to_string(),
RustType::U128 => "bigint".to_string(),
RustType::I128 => "bigint".to_string(),
RustType::USize => "bigint".to_string(),
RustType::ISize => "bigint".to_string(),
RustType::F32 => "number".to_string(),
RustType::F64 => "number".to_string(),
RustType::Bool => "boolean".to_string(),
RustType::String => "string".to_string(),
RustType::Option(t) => format!("{} | undefined", t.ts_type()),
RustType::Vec(t) => format!("Array<{}>", t.ts_type()),
RustType::TypedArray(t) => match t.as_ref() {
RustType::U8 => "Uint8Array".to_string(),
RustType::I8 => "Int8Array".to_string(),
RustType::U16 => "Uint16Array".to_string(),
RustType::I16 => "Int16Array".to_string(),
RustType::U32 => "Uint32Array".to_string(),
RustType::I32 => "Int32Array".to_string(),
RustType::U64 => "BigUint64Array".to_string(),
RustType::I64 => "BigInt64Array".to_string(),
RustType::USize => "BigUint64Array".to_string(),
RustType::ISize => "BigInt64Array".to_string(),
RustType::F32 => "Float32Array".to_string(),
RustType::F64 => "Float64Array".to_string(),
_ => panic!("Typed arrays not supported for this underlying type"),
},
RustType::Tuple(t) => format!(
"[{}]",
t.iter().map(|t| t.ts_type()).collect::<Vec<_>>().join(", ")
),
RustType::HashMap(k, v) => format!("Map<{}, {}>", k.ts_type(), v.ts_type()),
RustType::User(t) => t.to_string(),
}
}
pub fn writer(&self) -> String {
match self {
RustType::U8 => "writeU8".to_string(),
RustType::I8 => "writeI8".to_string(),
RustType::U16 => "writeU16".to_string(),
RustType::I16 => "writeI16".to_string(),
RustType::U32 => "writeU32".to_string(),
RustType::I32 => "writeI32".to_string(),
RustType::U64 => "writeU64".to_string(),
RustType::I64 => "writeI64".to_string(),
RustType::U128 => "writeU128".to_string(),
RustType::I128 => "writeI128".to_string(),
RustType::USize => "writeUSize".to_string(),
RustType::ISize => "writeISize".to_string(),
RustType::F32 => "writeF32".to_string(),
RustType::F64 => "writeF64".to_string(),
RustType::Bool => "writeBool".to_string(),
RustType::String => "writeString".to_string(),
RustType::Option(t) => format!("writeOption({})", t.writer()),
RustType::Vec(t) => format!("writeSeq({})", t.writer()),
RustType::TypedArray(_) => "writeTypedArray".to_string(),
RustType::Tuple(t) => format!(
"writeTuple<{}>({})",
self.ts_type(),
t.iter().map(|t| t.writer()).collect::<Vec<_>>().join(", ")
),
RustType::HashMap(k, v) => format!("writeMap({}, {})", k.writer(), v.writer()),
RustType::User(t) => format!("write{}", t),
}
}
pub fn reader(&self) -> String {
match self {
RustType::U8 => "readU8".to_string(),
RustType::I8 => "readI8".to_string(),
RustType::U16 => "readU16".to_string(),
RustType::I16 => "readI16".to_string(),
RustType::U32 => "readU32".to_string(),
RustType::I32 => "readI32".to_string(),
RustType::U64 => "readU64".to_string(),
RustType::I64 => "readI64".to_string(),
RustType::U128 => "readU128".to_string(),
RustType::I128 => "readI128".to_string(),
RustType::USize => "readUSize".to_string(),
RustType::ISize => "readISize".to_string(),
RustType::F32 => "readF32".to_string(),
RustType::F64 => "readF64".to_string(),
RustType::Bool => "readBool".to_string(),
RustType::String => "readString".to_string(),
RustType::Option(t) => format!("readOption({})", t.reader()),
RustType::Vec(t) => format!("readSeq({})", t.reader()),
RustType::TypedArray(_) => format!("readTypedArray({})", self.ts_type()),
RustType::Tuple(t) => format!(
"readTuple<{}>({})",
self.ts_type(),
t.iter().map(|t| t.reader()).collect::<Vec<_>>().join(", ")
),
RustType::HashMap(k, v) => format!("readMap({}, {})", k.reader(), v.reader()),
RustType::User(t) => format!("read{}", t),
}
}
}
fn get_generic_params(segment: &PathSegment) -> Vec<String> {
let mut output = vec![];
if let syn::PathArguments::AngleBracketed(ab) = &segment.arguments {
for arg in &ab.args {
if let syn::GenericArgument::Type(t) = &arg {
if let Type::Path(p) = t {
output.push(p.path.segments[0].ident.to_string());
let mut sub = get_generic_params(&p.path.segments[0]);
output.append(&mut sub);
}
}
}
}
output
}
impl From<Type> for RustType {
fn from(t: Type) -> Self {
if let Type::Path(path) = &t {
let mut types = vec![path.path.segments[0].ident.to_string()];
types.append(&mut get_generic_params(&path.path.segments[0]));
let mut cur_types: Vec<RustType> = vec![types.pop().unwrap().into()];
while let Some(s) = types.pop() {
if s == "Option" {
cur_types = vec![RustType::Option(Box::new(
cur_types.pop().expect("Could not find type for Option"),
))];
} else if s == "Vec" {
match cur_types[0] {
RustType::U8
| RustType::I8
| RustType::U16
| RustType::I16
| RustType::U32
| RustType::I32
| RustType::U64
| RustType::I64
| RustType::USize
| RustType::ISize
| RustType::F32
| RustType::F64 => {
cur_types = vec![RustType::TypedArray(Box::new(
cur_types.pop().expect("Could not find type for TypedArray"),
))];
}
_ => {
cur_types = vec![RustType::Vec(Box::new(
cur_types.pop().expect("Could not find type for Vec"),
))]
}
}
} else if s == "HashMap" {
cur_types = vec![RustType::HashMap(
Box::new(
cur_types
.pop()
.expect("Could not find type for HashMap key"),
),
Box::new(
cur_types
.pop()
.expect("Could not find type for HashMap value"),
),
)]
} else {
cur_types.push(s.into());
}
}
if cur_types.len() != 1 {
panic!("Not one type remaining");
}
cur_types.pop().expect("Could not work out type")
} else if let Type::Tuple(path) = &t {
RustType::Tuple(path.elems.clone().into_iter().map(|t| t.into()).collect())
} else {
panic!("Could not work out type: {:#?}", t);
}
}
}
impl From<&str> for RustType {
fn from(t: &str) -> Self {
match &t[..] {
"u8" => RustType::U8,
"i8" => RustType::I8,
"u16" => RustType::U16,
"i16" => RustType::I16,
"u32" => RustType::U32,
"i32" => RustType::I32,
"u64" => RustType::U64,
"i64" => RustType::I64,
"u128" => RustType::U128,
"i128" => RustType::I128,
"usize" => RustType::USize,
"isize" => RustType::ISize,
"f32" => RustType::F32,
"f64" => RustType::F64,
"bool" => RustType::Bool,
"String" => RustType::String,
o => RustType::User(o.to_string()),
}
}
}
impl From<String> for RustType {
fn from(t: String) -> Self {
t[..].into()
}
}