Skip to content

Commit

Permalink
Func types (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasavila00 authored Nov 1, 2024
1 parent 72eab85 commit 6a33a81
Show file tree
Hide file tree
Showing 28 changed files with 194 additions and 66 deletions.
2 changes: 1 addition & 1 deletion e2e-tests/bug-rust-repro/src/generated/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import {printErrors} from '@beff/client';
import {z} from 'zod';
import validatorsMod from "./validators.js"; const { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators, c } = validatorsMod;
import validatorsMod from "./validators.js"; const { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeFunction, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators, c } = validatorsMod;
const RequiredCustomFormats = ["ValidCurrency"];
const buildParsersInput = {
"A": function(ctx, input, required = true) {
Expand Down
12 changes: 11 additions & 1 deletion e2e-tests/bug-rust-repro/src/generated/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ function decodeNumber(ctx, input, required) {
return buildError(input, ctx, "expected number");
}

function decodeFunction(ctx, input, required) {
if (!required && input == null) {
return input;
}
if (typeof input === "function") {
return input;
}
return buildError(input, ctx, "expected function");
}

function decodeCodec(ctx, input, required, codec) {
if (!required && input == null) {
return input;
Expand Down Expand Up @@ -336,4 +346,4 @@ const validators = {
A: DecodeA
};

export default { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators };
export default { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeFunction, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators };
8 changes: 8 additions & 0 deletions e2e-tests/standalone-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# node-server

## 1.0.54

### Patch Changes

- Updated dependencies
- @beff/cli@0.0.57
- @beff/client@0.0.57

## 1.0.53

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/standalone-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "standalone-parser",
"version": "1.0.53",
"version": "1.0.54",
"description": "",
"main": "index.js",
"scripts": {
Expand All @@ -11,7 +11,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@beff/cli": "workspace:^0.0.56",
"@beff/cli": "workspace:^0.0.57",
"@beff/client": "workspace:^",
"vitest": "^0.34.4",
"zod": "^3.23.5"
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/standalone-parser/src/generated/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import {printErrors} from '@beff/client';
import {z} from 'zod';
import validatorsMod from "./validators.js"; const { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators, c } = validatorsMod;
import validatorsMod from "./validators.js"; const { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeFunction, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators, c } = validatorsMod;
const RequiredCustomFormats = ["ValidCurrency"];
const buildParsersInput = {
"AObject": function(ctx, input, required = true) {
Expand Down
12 changes: 11 additions & 1 deletion e2e-tests/standalone-parser/src/generated/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ function decodeNumber(ctx, input, required) {
return buildError(input, ctx, "expected number");
}

function decodeFunction(ctx, input, required) {
if (!required && input == null) {
return input;
}
if (typeof input === "function") {
return input;
}
return buildError(input, ctx, "expected function");
}

function decodeCodec(ctx, input, required, codec) {
if (!required && input == null) {
return input;
Expand Down Expand Up @@ -677,4 +687,4 @@ const validators = {
BObject: DecodeBObject
};

export default { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators };
export default { decodeObject, decodeArray, decodeString, decodeNumber, decodeCodec, decodeFunction, decodeStringWithFormat, decodeAnyOf, decodeAllOf, decodeBoolean, decodeAny, decodeTuple, decodeNull, decodeConst, registerCustomFormatter, validators };
6 changes: 6 additions & 0 deletions packages/beff-cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @beff/cli

## 0.0.57

### Patch Changes

- func types

## 0.0.56

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/beff-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beff/cli",
"version": "0.0.56",
"version": "0.0.57",
"description": "",
"bin": {
"beff": "./bin/index.js"
Expand Down
8 changes: 8 additions & 0 deletions packages/beff-client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @beff/client

## 0.0.57

### Patch Changes

- func types
- Updated dependencies
- @beff/cli@0.0.57

## 0.0.56

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/beff-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@beff/client",
"version": "0.0.56",
"version": "0.0.57",
"description": "",
"main": "dist/cjs/index.js",
"scripts": {
Expand All @@ -20,7 +20,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@beff/cli": "workspace:^0.0.56",
"@beff/cli": "workspace:^0.0.57",
"zod": "^3.23.5"
},
"devDependencies": {
Expand Down
113 changes: 73 additions & 40 deletions packages/beff-core/src/ast/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use swc_ecma_ast::TplElement;
use swc_ecma_ast::TsArrayType;
use swc_ecma_ast::TsEntityName;
use swc_ecma_ast::TsFnParam;
use swc_ecma_ast::TsFnType;
use swc_ecma_ast::TsIndexSignature;
use swc_ecma_ast::TsIntersectionType;
use swc_ecma_ast::TsKeywordType;
Expand Down Expand Up @@ -247,6 +248,7 @@ pub enum JsonSchema {
// semantic types
StNever,
StNot(Box<JsonSchema>),
Function,
}

#[derive(PartialEq, Eq, Hash, Debug, Ord, PartialOrd, Clone)]
Expand Down Expand Up @@ -410,26 +412,27 @@ impl<'a> JsonFlatConverter<'a> {
}

#[allow(clippy::cast_precision_loss)]
pub fn to_json_flat(&mut self, schema: JsonSchema) -> Json {
pub fn to_json_flat(&mut self, schema: JsonSchema) -> anyhow::Result<Json> {
match schema {
JsonSchema::String => {
Json::object(vec![("type".into(), Json::String("string".into()))])
}
JsonSchema::StringWithFormat(format) => Json::object(vec![
JsonSchema::String => Ok(Json::object(vec![(
"type".into(),
Json::String("string".into()),
)])),
JsonSchema::StringWithFormat(format) => Ok(Json::object(vec![
("type".into(), Json::String("string".into())),
("format".into(), Json::String(format)),
]),
JsonSchema::Codec(format) => Json::object(vec![
])),
JsonSchema::Codec(format) => Ok(Json::object(vec![
("type".into(), Json::String("string".into())),
("format".into(), Json::String(format.to_string())),
]),
JsonSchema::TplLitType(items) => Json::object(vec![
])),
JsonSchema::TplLitType(items) => Ok(Json::object(vec![
("type".into(), Json::String("string".into())),
(
"format".into(),
Json::String(TplLitTypeItem::describe_vec(&items)),
),
]),
])),

JsonSchema::Object { vs: values, rest } => {
let mut vs = vec![
Expand All @@ -452,37 +455,39 @@ impl<'a> JsonFlatConverter<'a> {
Json::Object(
values
.into_iter()
.map(|(k, v)| (k, self.to_json_flat(v.inner_move())))
.collect(),
.map(|(k, v)| self.to_json_flat(v.inner_move()).map(|v| (k, v)))
.collect::<anyhow::Result<BTreeMap<_, _>>>()?,
),
),
];

if let Some(rest) = rest {
vs.push(("additionalProperties".into(), self.to_json_flat(*rest)));
vs.push(("additionalProperties".into(), self.to_json_flat(*rest)?));
} else {
vs.push(("additionalProperties".into(), Json::Bool(false)));
}
Json::object(vs)
Ok(Json::object(vs))
}
JsonSchema::Array(typ) => {
Json::object(vec![
Ok(Json::object(vec![
//
("type".into(), Json::String("array".into())),
("items".into(), self.to_json_flat(*typ)),
])
}
JsonSchema::Boolean => {
Json::object(vec![("type".into(), Json::String("boolean".into()))])
("items".into(), self.to_json_flat(*typ)?),
]))
}
JsonSchema::Number => {
Json::object(vec![("type".into(), Json::String("number".into()))])
}
JsonSchema::Any => Json::object(vec![]),
JsonSchema::Boolean => Ok(Json::object(vec![(
"type".into(),
Json::String("boolean".into()),
)])),
JsonSchema::Number => Ok(Json::object(vec![(
"type".into(),
Json::String("number".into()),
)])),
JsonSchema::Any => Ok(Json::object(vec![])),
JsonSchema::Ref(reference) => {
if self.seen_refs.contains(&reference) {
// recursive type, let's return "ANY"
return Json::object(vec![]);
return Ok(Json::object(vec![]));
}
let validator = self
.validators
Expand All @@ -496,7 +501,10 @@ impl<'a> JsonFlatConverter<'a> {
json
}

JsonSchema::Null => Json::object(vec![("type".into(), Json::String("null".into()))]),
JsonSchema::Null => Ok(Json::object(vec![(
"type".into(),
Json::String("null".into()),
)])),
JsonSchema::AnyOf(types) => {
let all_literals = types.iter().all(|it| matches!(it, JsonSchema::Const(_)));
if all_literals {
Expand All @@ -510,22 +518,30 @@ impl<'a> JsonFlatConverter<'a> {

let all_strings = vs.iter().all(|it| matches!(it, Json::String(_)));
if all_strings {
Json::object(vec![
Ok(Json::object(vec![
("enum".into(), Json::Array(vs)),
("type".into(), Json::String("string".into())),
])
]))
} else {
Json::object(vec![("enum".into(), Json::Array(vs))])
Ok(Json::object(vec![("enum".into(), Json::Array(vs))]))
}
} else {
let vs = types.into_iter().map(|it| self.to_json_flat(it)).collect();
Json::object(vec![("anyOf".into(), Json::Array(vs))])
let vs = types
.into_iter()
.map(|it| self.to_json_flat(it))
.collect::<anyhow::Result<_>>()?;
Ok(Json::object(vec![("anyOf".into(), Json::Array(vs))]))
}
}
JsonSchema::AllOf(types) => Json::object(vec![(
"allOf".into(),
Json::Array(types.into_iter().map(|it| self.to_json_flat(it)).collect()),
)]),
JsonSchema::AllOf(types) => {
let mut arr_types = vec![];

for t in types {
arr_types.push(self.to_json_flat(t)?);
}

Ok(Json::object(vec![("allOf".into(), Json::Array(arr_types))]))
}

JsonSchema::Tuple {
prefix_items,
Expand All @@ -543,25 +559,26 @@ impl<'a> JsonFlatConverter<'a> {
prefix_items
.into_iter()
.map(|it| self.to_json_flat(it))
.collect(),
.collect::<anyhow::Result<_>>()?,
),
));
}
if let Some(ty) = items {
v.push(("items".into(), self.to_json_flat(*ty)));
v.push(("items".into(), self.to_json_flat(*ty)?));
} else {
v.push(("minItems".into(), Json::parse_int(len_f as i64)));
v.push(("maxItems".into(), Json::parse_int(len_f as i64)));
}
Json::object(v)
Ok(Json::object(v))
}
JsonSchema::Const(val) => Json::object(vec![("const".into(), val.to_json())]),
JsonSchema::Const(val) => Ok(Json::object(vec![("const".into(), val.to_json())])),
JsonSchema::AnyArrayLike => {
self.to_json_flat(JsonSchema::Array(JsonSchema::Any.into()))
}
JsonSchema::StNever | JsonSchema::StNot(_) => {
unreachable!("semantic types should not be converted to json")
Err(anyhow!("semantic types should not be converted to json"))
}
JsonSchema::Function => Err(anyhow!("function type is not supported in json schema"))?,
}
}
}
Expand Down Expand Up @@ -1005,6 +1022,22 @@ impl JsonSchema {
}),
})
}
JsonSchema::Function => TsType::TsFnOrConstructorType(
swc_ecma_ast::TsFnOrConstructorType::TsFnType(TsFnType {
span: DUMMY_SP,
params: vec![],
type_params: None,
type_ann: TsTypeAnn {
span: DUMMY_SP,
type_ann: TsType::TsKeywordType(TsKeywordType {
span: DUMMY_SP,
kind: TsKeywordTypeKind::TsVoidKeyword,
})
.into(),
}
.into(),
}),
),
}
}
}
8 changes: 4 additions & 4 deletions packages/beff-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,10 @@ pub struct Validator {
pub schema: JsonSchema,
}
impl Validator {
pub fn to_json_kv(&self, validators: &[Validator]) -> Vec<(String, Json)> {
vec![(
pub fn to_json_kv(&self, validators: &[Validator]) -> anyhow::Result<Vec<(String, Json)>> {
Ok(vec![(
self.name.clone(),
JsonFlatConverter::new(validators).to_json_flat(self.schema.clone()),
)]
JsonFlatConverter::new(validators).to_json_flat(self.schema.clone())?,
)])
}
}
8 changes: 4 additions & 4 deletions packages/beff-core/src/parser_extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ pub struct BuiltDecoder {
pub schema: JsonSchema,
}
impl BuiltDecoder {
pub fn to_json_kv(&self, validators: &[Validator]) -> Vec<(String, Json)> {
vec![(
pub fn to_json_kv(&self, validators: &[Validator]) -> anyhow::Result<Vec<(String, Json)>> {
Ok(vec![(
self.exported_name.clone(),
JsonFlatConverter::new(validators).to_json_flat(self.schema.clone()),
)]
JsonFlatConverter::new(validators).to_json_flat(self.schema.clone())?,
)])
}
}

Expand Down
Loading

0 comments on commit 6a33a81

Please sign in to comment.