Skip to content

Commit

Permalink
tsparser: support enums
Browse files Browse the repository at this point in the history
  • Loading branch information
eandre committed Jun 8, 2024
1 parent 6f1d8de commit 51f1fce
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 49 deletions.
22 changes: 21 additions & 1 deletion tsparser/src/legacymeta/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use crate::parser::parser::ParseContext;
use crate::parser::resources::apis::api::Endpoint;
use crate::parser::types::custom::{resolve_custom_type_named, CustomType};
use crate::parser::types::{
drop_empty_or_void, Basic, FieldName, Generic, Interface, Literal, Named, ObjectId, Type,
drop_empty_or_void, Basic, EnumValue, FieldName, Generic, Interface, Literal, Named, ObjectId,
Type,
};
use crate::parser::{FilePath, FileSet, Range};

Expand Down Expand Up @@ -88,6 +89,25 @@ impl<'a, 'b> BuilderCtx<'a, 'b> {
}
Type::Interface(tt) => self.interface(tt)?,

Type::Enum(tt) => schema::Type {
// Treat this as a union.
typ: Some(styp::Typ::Union(schema::Union {
types: tt
.members
.iter()
.cloned()
.map(|m| schema::Type {
typ: Some(styp::Typ::Literal(schema::Literal {
value: Some(match m.value {
EnumValue::String(str) => schema::literal::Value::Str(str),
EnumValue::Number(n) => schema::literal::Value::Int(n),
}),
})),
})
.collect(),
})),
},

Type::Union(types) => schema::Type {
typ: Some(styp::Typ::Union(schema::Union {
types: self.types(types)?,
Expand Down
4 changes: 2 additions & 2 deletions tsparser/src/parser/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ mod tests;

pub use object::{Object, ObjectId, ObjectKind, ResolveState};
pub use typ::{
Basic, ClassType, FieldName, Generic, Interface, InterfaceField, Literal, Named, Type,
TypeArgId,
Basic, ClassType, EnumMember, EnumType, EnumValue, FieldName, Generic, Interface,
InterfaceField, Literal, Named, Type, TypeArgId,
};
pub use type_resolve::TypeChecker;
pub use utils::*;
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,80 @@ input_file: tsparser/src/parser/types/testdata/basic.ts
call: None,
},
),
"Enum1": Enum(
EnumType {
members: [
EnumMember {
name: "A",
value: Number(
0,
),
},
EnumMember {
name: "B",
value: Number(
1,
),
},
EnumMember {
name: "C",
value: Number(
2,
),
},
EnumMember {
name: "D",
value: String(
"foo",
),
},
EnumMember {
name: "E",
value: Number(
5,
),
},
EnumMember {
name: "F",
value: Number(
6,
),
},
],
},
),
"EnumFields": Union(
[
Literal(
String(
"A",
),
),
Literal(
String(
"B",
),
),
Literal(
String(
"C",
),
),
Literal(
String(
"D",
),
),
Literal(
String(
"E",
),
),
Literal(
String(
"F",
),
),
],
),
}
11 changes: 11 additions & 0 deletions tsparser/src/parser/types/testdata/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ export type Intersect2 = {foo: string} & {foo: "literal"};
export type Intersect3 = {foo: string} & {foo: number};
export type Intersect4 = {foo?: "optional"} & {foo: string};
export type Intersect5 = {a: string; b: string; c: string} & {a: any; b: unknown; c: never};

// Enums
export enum Enum1 {
A,
B,
C,
D = "foo",
E = 5,
F,
}
export type EnumFields = keyof typeof Enum1;
84 changes: 84 additions & 0 deletions tsparser/src/parser/types/typ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub enum Type {
Literal(Literal),
/// class Foo {}
Class(ClassType),
/// enum Foo {}
Enum(EnumType),

/// A named type, with optional type arguments.
Named(Named),
Expand Down Expand Up @@ -62,6 +64,7 @@ impl Type {
(Type::Optional(a), Type::Optional(b)) => a.identical(b),
(Type::This, Type::This) => true,
(Type::Generic(a), Type::Generic(b)) => a.identical(b),
(Type::Enum(a), Type::Enum(b)) => a.identical(b),
_ => false,
}
}
Expand Down Expand Up @@ -287,6 +290,45 @@ impl ClassType {
}
}

#[derive(Debug, Clone, Hash, Serialize, Eq, PartialEq)]
pub struct EnumType {
pub members: Vec<EnumMember>,
}

#[derive(Debug, Clone, Hash, Serialize, Eq, PartialEq)]
pub struct EnumMember {
pub name: String,
pub value: EnumValue,
}

#[derive(Debug, Clone, Hash, Serialize, Eq, PartialEq)]
pub enum EnumValue {
String(String),
Number(i64),
}

impl EnumValue {
pub fn to_literal(self) -> Literal {
match self {
EnumValue::String(s) => Literal::String(s),
EnumValue::Number(n) => Literal::Number(n as f64),
}
}

pub fn to_type(self) -> Type {
Type::Literal(self.to_literal())
}
}

impl EnumType {
pub fn identical(&self, other: &EnumType) -> bool {
if self.members.len() != other.members.len() {
return false;
}
*self == *other
}
}

#[derive(Debug, Clone, Serialize)]
pub struct Named {
pub obj: Rc<object::Object>,
Expand Down Expand Up @@ -521,6 +563,48 @@ impl Type {
_ => Some(false),
},

(Type::Enum(a), other) => {
let this_fields: HashMap<&str, &EnumValue> =
HashMap::from_iter(a.members.iter().map(|m| (m.name.as_str(), &m.value)));
match other {
Type::Enum(other) => {
// Does every field in `other` exist in `this_fields`?
for mem in &other.members {
if let Some(this_field) = this_fields.get(mem.name.as_str()) {
if **this_field == mem.value {
continue;
}
}
return Some(false);
}
Some(true)
}

Type::Interface(other) => {
// Does every field in `other` exist in `iface`?
let mut found_none = false;
for field in &other.fields {
if let FieldName::String(name) = &field.name {
if let Some(this_field) = this_fields.get(name.as_str()) {
let this_typ = (*this_field).clone().to_type();
match this_typ.assignable(state, &field.typ) {
Some(true) => continue,
Some(false) => return Some(false),
None => found_none = true,
}
}
}
}
if found_none {
None
} else {
Some(true)
}
}
_ => Some(false),
}
}

(Type::Interface(iface), other) => {
let this_fields: HashMap<&FieldName, &InterfaceField> =
HashMap::from_iter(iface.fields.iter().map(|f| (&f.name, f)));
Expand Down
Loading

0 comments on commit 51f1fce

Please sign in to comment.