bluejay 0.1.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Cargo.lock +423 -0
- data/Cargo.toml +2 -0
- data/LICENSE +21 -0
- data/README.md +33 -0
- data/ext/Cargo.toml +17 -0
- data/ext/extconf.rb +6 -0
- data/ext/src/execution/coerce_result.rs +54 -0
- data/ext/src/execution/engine.rs +466 -0
- data/ext/src/execution/execution_error.rs +28 -0
- data/ext/src/execution/field_error.rs +8 -0
- data/ext/src/execution/key_store.rs +23 -0
- data/ext/src/execution.rs +10 -0
- data/ext/src/helpers/public_name.rs +21 -0
- data/ext/src/helpers/typed_frozen_r_array.rs +56 -0
- data/ext/src/helpers/wrapped_definition.rs +79 -0
- data/ext/src/helpers/wrapped_struct.rs +97 -0
- data/ext/src/helpers.rs +8 -0
- data/ext/src/lib.rs +10 -0
- data/ext/src/ruby_api/arguments_definition.rs +8 -0
- data/ext/src/ruby_api/coerce_input.rs +6 -0
- data/ext/src/ruby_api/coercion_error.rs +61 -0
- data/ext/src/ruby_api/custom_scalar_type_definition.rs +62 -0
- data/ext/src/ruby_api/enum_type_definition.rs +107 -0
- data/ext/src/ruby_api/enum_value_definition.rs +58 -0
- data/ext/src/ruby_api/enum_value_definitions.rs +8 -0
- data/ext/src/ruby_api/execution_error.rs +48 -0
- data/ext/src/ruby_api/execution_result.rs +40 -0
- data/ext/src/ruby_api/field_definition.rs +112 -0
- data/ext/src/ruby_api/fields_definition.rs +8 -0
- data/ext/src/ruby_api/input_fields_definition.rs +8 -0
- data/ext/src/ruby_api/input_object_type_definition.rs +138 -0
- data/ext/src/ruby_api/input_type_reference.rs +358 -0
- data/ext/src/ruby_api/input_value_definition.rs +98 -0
- data/ext/src/ruby_api/interface_implementation.rs +42 -0
- data/ext/src/ruby_api/interface_implementations.rs +8 -0
- data/ext/src/ruby_api/interface_type_definition.rs +82 -0
- data/ext/src/ruby_api/json_value.rs +111 -0
- data/ext/src/ruby_api/object_type_definition.rs +100 -0
- data/ext/src/ruby_api/output_type_reference.rs +238 -0
- data/ext/src/ruby_api/r_result.rs +84 -0
- data/ext/src/ruby_api/scalar.rs +45 -0
- data/ext/src/ruby_api/schema_definition.rs +270 -0
- data/ext/src/ruby_api/union_member_type.rs +41 -0
- data/ext/src/ruby_api/union_member_types.rs +8 -0
- data/ext/src/ruby_api/union_type_definition.rs +89 -0
- data/ext/src/ruby_api/validation_error.rs +63 -0
- data/ext/src/ruby_api.rs +75 -0
- data/lib/bluejay/base_input_type_reference.rb +13 -0
- data/lib/bluejay/base_output_type_reference.rb +15 -0
- data/lib/bluejay/custom_scalar_type.rb +40 -0
- data/lib/bluejay/enum_type.rb +44 -0
- data/lib/bluejay/ext.bundle +0 -0
- data/lib/bluejay/finalize.rb +27 -0
- data/lib/bluejay/input_type.rb +64 -0
- data/lib/bluejay/input_type_reference_shorthands.rb +28 -0
- data/lib/bluejay/interface_type.rb +60 -0
- data/lib/bluejay/json_value.rb +16 -0
- data/lib/bluejay/name_from_class.rb +18 -0
- data/lib/bluejay/object_type.rb +68 -0
- data/lib/bluejay/output_type_reference_shorthands.rb +28 -0
- data/lib/bluejay/schema.rb +63 -0
- data/lib/bluejay/union_type.rb +43 -0
- data/lib/bluejay/version.rb +5 -0
- data/lib/bluejay.rb +28 -0
- data/lib/rbi_ext/model.rb +64 -0
- data/lib/tapioca/dsl/compilers/input_type.rb +34 -0
- data/lib/tapioca/dsl/compilers/interface_type.rb +29 -0
- data/lib/tapioca/dsl/compilers/object_type.rb +43 -0
- data/lib/tapioca/dsl/compilers/schema.rb +42 -0
- metadata +131 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
use super::root;
|
2
|
+
use super::{output_type_reference::{OutputTypeReference, BaseOutputTypeReference}, arguments_definition::ArgumentsDefinition};
|
3
|
+
use crate::helpers::{WrappedStruct};
|
4
|
+
use magnus::{function, Error, Module, Object, scan_args::get_kwargs, RHash, RArray, Value, TypedData, DataTypeFunctions, memoize, value::BoxValue, gc, method};
|
5
|
+
use convert_case::{Case, Casing};
|
6
|
+
use bluejay_core::{BuiltinScalarDefinition, definition::{OutputTypeReference as CoreOutputTypeReference}};
|
7
|
+
|
8
|
+
#[derive(Debug, TypedData)]
|
9
|
+
#[magnus(class = "Bluejay::FieldDefinition", mark)]
|
10
|
+
pub struct FieldDefinition {
|
11
|
+
name: String,
|
12
|
+
description: Option<String>,
|
13
|
+
arguments_definition: ArgumentsDefinition,
|
14
|
+
r#type: WrappedStruct<OutputTypeReference>,
|
15
|
+
is_builtin: bool,
|
16
|
+
ruby_resolver_method_name: String,
|
17
|
+
}
|
18
|
+
|
19
|
+
impl FieldDefinition {
|
20
|
+
pub fn new(kw: RHash) -> Result<Self, Error> {
|
21
|
+
let args = get_kwargs(kw, &["name", "type"], &["argument_definitions", "description"])?;
|
22
|
+
let (name, r#type): (String, WrappedStruct<OutputTypeReference>) = args.required;
|
23
|
+
let (argument_definitions, description): (Option<RArray>, Option<Option<String>>) = args.optional;
|
24
|
+
let _: () = args.splat;
|
25
|
+
let arguments_definition = ArgumentsDefinition::new(argument_definitions.unwrap_or_else(|| RArray::new()))?;
|
26
|
+
let description = description.unwrap_or_default();
|
27
|
+
let ruby_resolver_method_name = format!("resolve_{}", name.to_case(Case::Snake));
|
28
|
+
Ok(Self { name, description, arguments_definition, r#type, is_builtin: false, ruby_resolver_method_name })
|
29
|
+
}
|
30
|
+
|
31
|
+
pub(crate) fn typename() -> WrappedStruct<Self> {
|
32
|
+
memoize!((BoxValue<Value>, BoxValue<Value>, WrappedStruct<FieldDefinition>): {
|
33
|
+
let t = WrappedStruct::wrap(CoreOutputTypeReference::Base(BaseOutputTypeReference::BuiltinScalarType(BuiltinScalarDefinition::String), true).into());
|
34
|
+
let fd = Self {
|
35
|
+
name: "__typename".to_string(),
|
36
|
+
description: None,
|
37
|
+
arguments_definition: ArgumentsDefinition::empty(),
|
38
|
+
r#type: t,
|
39
|
+
is_builtin: true,
|
40
|
+
ruby_resolver_method_name: "resolve_typename".to_string(),
|
41
|
+
};
|
42
|
+
let ws = WrappedStruct::wrap(fd);
|
43
|
+
(BoxValue::new(ws.to_value()), BoxValue::new(t.to_value()), ws)
|
44
|
+
}).2
|
45
|
+
}
|
46
|
+
|
47
|
+
pub(crate) fn ruby_resolver_method_name(&self) -> &str {
|
48
|
+
self.ruby_resolver_method_name.as_str()
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
impl DataTypeFunctions for FieldDefinition {
|
53
|
+
fn mark(&self) {
|
54
|
+
gc::mark(self.arguments_definition);
|
55
|
+
self.r#type.mark();
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
impl FieldDefinition {
|
60
|
+
pub fn name(&self) -> &str {
|
61
|
+
self.name.as_str()
|
62
|
+
}
|
63
|
+
|
64
|
+
pub fn description(&self) -> Option<&str> {
|
65
|
+
self.description.as_ref().map(String::as_str)
|
66
|
+
}
|
67
|
+
|
68
|
+
pub fn argument_definitions(&self) -> &ArgumentsDefinition {
|
69
|
+
&self.arguments_definition
|
70
|
+
}
|
71
|
+
|
72
|
+
pub fn r#type(&self) -> &OutputTypeReference {
|
73
|
+
self.r#type.get()
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
impl bluejay_core::definition::FieldDefinition for FieldDefinition {
|
78
|
+
type ArgumentsDefinition = ArgumentsDefinition;
|
79
|
+
type OutputTypeReference = OutputTypeReference;
|
80
|
+
|
81
|
+
fn description(&self) -> Option<&str> {
|
82
|
+
self.description.as_ref().map(String::as_str)
|
83
|
+
}
|
84
|
+
|
85
|
+
fn name(&self) -> &str {
|
86
|
+
self.name.as_str()
|
87
|
+
}
|
88
|
+
|
89
|
+
fn arguments_definition(&self) -> &Self::ArgumentsDefinition {
|
90
|
+
&self.arguments_definition
|
91
|
+
}
|
92
|
+
|
93
|
+
fn r#type(&self) -> &Self::OutputTypeReference {
|
94
|
+
self.r#type.get()
|
95
|
+
}
|
96
|
+
|
97
|
+
fn is_builtin(&self) -> bool {
|
98
|
+
self.is_builtin
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
pub fn init() -> Result<(), Error> {
|
103
|
+
let class = root().define_class("FieldDefinition", Default::default())?;
|
104
|
+
|
105
|
+
class.define_singleton_method("new", function!(FieldDefinition::new, 1))?;
|
106
|
+
class.define_method("name", method!(FieldDefinition::name, 0))?;
|
107
|
+
class.define_method("resolver_method_name", method!(FieldDefinition::ruby_resolver_method_name, 0))?;
|
108
|
+
class.define_method("argument_definitions", method!(|fd: &FieldDefinition| -> RArray { (*fd.argument_definitions()).into() }, 0))?;
|
109
|
+
class.define_method("type", method!(|fd: &FieldDefinition| fd.r#type, 0))?;
|
110
|
+
|
111
|
+
Ok(())
|
112
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
use super::{field_definition::FieldDefinition};
|
2
|
+
use crate::helpers::{WrappedStruct, TypedFrozenRArray};
|
3
|
+
|
4
|
+
pub type FieldsDefinition = TypedFrozenRArray<WrappedStruct<FieldDefinition>>;
|
5
|
+
|
6
|
+
impl bluejay_core::definition::FieldsDefinition for FieldsDefinition {
|
7
|
+
type FieldDefinition = FieldDefinition;
|
8
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
use super::{input_value_definition::InputValueDefinition};
|
2
|
+
use crate::helpers::{WrappedStruct, TypedFrozenRArray};
|
3
|
+
|
4
|
+
pub type InputFieldsDefinition = TypedFrozenRArray<WrappedStruct<InputValueDefinition>>;
|
5
|
+
|
6
|
+
impl bluejay_core::definition::InputFieldsDefinition for InputFieldsDefinition {
|
7
|
+
type InputValueDefinition = InputValueDefinition;
|
8
|
+
}
|
@@ -0,0 +1,138 @@
|
|
1
|
+
use std::{collections::HashSet};
|
2
|
+
use bluejay_core::AsIter;
|
3
|
+
use magnus::{function, Error, Module, Object, scan_args::get_kwargs, RHash, Value, method, TypedData, RArray, DataTypeFunctions, RClass, gc, QNIL, memoize};
|
4
|
+
use super::{root, input_fields_definition::InputFieldsDefinition, r_result::RResult, coerce_input::CoerceInput, coercion_error::CoercionError};
|
5
|
+
use crate::helpers::{public_name, HasDefinitionWrapper};
|
6
|
+
|
7
|
+
#[derive(Debug, TypedData)]
|
8
|
+
#[magnus(class = "Bluejay::InputObjectTypeDefinition", mark)]
|
9
|
+
pub struct InputObjectTypeDefinition {
|
10
|
+
name: String,
|
11
|
+
description: Option<String>,
|
12
|
+
input_fields_definition: InputFieldsDefinition,
|
13
|
+
input_value_definition_names: HashSet<String>,
|
14
|
+
ruby_class: RClass
|
15
|
+
}
|
16
|
+
|
17
|
+
impl InputObjectTypeDefinition {
|
18
|
+
fn new(kw: RHash) -> Result<Self, Error> {
|
19
|
+
let args = get_kwargs(kw, &["name", "input_field_definitions", "description", "ruby_class"], &[])?;
|
20
|
+
let (name, input_field_definitions, description, ruby_class): (String, RArray, Option<String>, RClass) = args.required;
|
21
|
+
let _: () = args.optional;
|
22
|
+
let _: () = args.splat;
|
23
|
+
let input_fields_definition = InputFieldsDefinition::new(input_field_definitions)?;
|
24
|
+
let input_value_definition_names = HashSet::from_iter(input_fields_definition.iter().map(|ivd| ivd.name().to_owned()));
|
25
|
+
Ok(Self { name, description, input_fields_definition, input_value_definition_names, ruby_class })
|
26
|
+
}
|
27
|
+
|
28
|
+
pub fn name(&self) -> &str {
|
29
|
+
self.name.as_str()
|
30
|
+
}
|
31
|
+
|
32
|
+
pub fn description(&self) -> Option<&str> {
|
33
|
+
self.description.as_ref().map(String::as_str)
|
34
|
+
}
|
35
|
+
|
36
|
+
pub fn input_fields_definition(&self) -> &InputFieldsDefinition {
|
37
|
+
&self.input_fields_definition
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
impl DataTypeFunctions for InputObjectTypeDefinition {
|
42
|
+
fn mark(&self) {
|
43
|
+
gc::mark(self.input_fields_definition);
|
44
|
+
gc::mark(self.ruby_class);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
impl HasDefinitionWrapper for InputObjectTypeDefinition {
|
49
|
+
fn wrapping_class() -> RClass {
|
50
|
+
*memoize!(RClass: root().define_class("InputType", Default::default()).unwrap())
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
impl bluejay_core::definition::InputObjectTypeDefinition for InputObjectTypeDefinition {
|
55
|
+
type InputFieldsDefinition = InputFieldsDefinition;
|
56
|
+
|
57
|
+
fn description(&self) -> Option<&str> {
|
58
|
+
self.description.as_ref().map(String::as_str)
|
59
|
+
}
|
60
|
+
|
61
|
+
fn name(&self) -> &str {
|
62
|
+
self.name.as_str()
|
63
|
+
}
|
64
|
+
|
65
|
+
fn input_field_definitions(&self) -> &Self::InputFieldsDefinition {
|
66
|
+
&self.input_fields_definition
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
impl CoerceInput for InputObjectTypeDefinition {
|
71
|
+
fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
|
72
|
+
if let Some(hash) = RHash::from_value(value) {
|
73
|
+
let args = RArray::new();
|
74
|
+
let mut errors = Vec::new();
|
75
|
+
|
76
|
+
for ivd in self.input_fields_definition.iter() {
|
77
|
+
let value = hash.get(ivd.name());
|
78
|
+
let required = ivd.is_required();
|
79
|
+
let default_value: Option<Value> = ivd.default_value().map(|v| v.clone().into());
|
80
|
+
if required && value.is_none() {
|
81
|
+
errors.push(CoercionError::new(
|
82
|
+
format!("No value for required field {}", ivd.name()),
|
83
|
+
path.to_owned(),
|
84
|
+
));
|
85
|
+
} else {
|
86
|
+
if value.is_none() && default_value.is_some() {
|
87
|
+
args.push(default_value.unwrap()).unwrap();
|
88
|
+
} else {
|
89
|
+
let mut inner_path = path.to_owned();
|
90
|
+
inner_path.push(ivd.name().to_owned());
|
91
|
+
match ivd.coerce_input(value.unwrap_or(*QNIL), &inner_path)? {
|
92
|
+
Ok(coerced_value) => { args.push(coerced_value).unwrap(); },
|
93
|
+
Err(errs) => { errors.extend(errs); },
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
let keys: Vec<String> = hash.check_funcall("keys", ()).unwrap()?;
|
100
|
+
|
101
|
+
errors.extend(keys.iter().filter_map(|key| {
|
102
|
+
if !self.input_value_definition_names.contains(key) {
|
103
|
+
Some(CoercionError::new(
|
104
|
+
format!("No field named `{}` on {}", key, self.name),
|
105
|
+
path.to_owned(),
|
106
|
+
))
|
107
|
+
} else {
|
108
|
+
None
|
109
|
+
}
|
110
|
+
}));
|
111
|
+
|
112
|
+
if errors.is_empty() {
|
113
|
+
self.ruby_class.new_instance(unsafe { args.as_slice() }).map(Ok)
|
114
|
+
} else {
|
115
|
+
Ok(Err(errors))
|
116
|
+
}
|
117
|
+
} else if value.is_kind_of(self.ruby_class) {
|
118
|
+
// TODO: this is kind of a hack for when a coerced variable value is nested in an uncoerced input
|
119
|
+
// see if there is a less hacky way to do this
|
120
|
+
Ok(Ok(value))
|
121
|
+
} else {
|
122
|
+
Ok(Err(vec![CoercionError::new(
|
123
|
+
format!("No implicit conversion of {} to {}", public_name(value), self.name),
|
124
|
+
path.to_owned(),
|
125
|
+
)]))
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
pub fn init() -> Result<(), Error> {
|
131
|
+
let class = root().define_class("InputObjectTypeDefinition", Default::default())?;
|
132
|
+
|
133
|
+
class.define_singleton_method("new", function!(InputObjectTypeDefinition::new, 1))?;
|
134
|
+
class.define_method("coerce_input", method!(|itd: &InputObjectTypeDefinition, input: Value| -> Result<RResult, Error> { itd.coerce_input(input, &[]).map(Into::into) }, 1))?;
|
135
|
+
class.define_method("input_field_definitions", method!(|itd: &InputObjectTypeDefinition| -> RArray { (*itd.input_fields_definition()).into() }, 0))?;
|
136
|
+
|
137
|
+
Ok(())
|
138
|
+
}
|
@@ -0,0 +1,358 @@
|
|
1
|
+
use magnus::{Error, Value, exception, TypedData, DataTypeFunctions, Module, scan_args::get_kwargs, RHash, Object, function, Integer, RString, Float, RArray, method};
|
2
|
+
use super::{root, coerce_input::CoerceInput, coercion_error::CoercionError, input_object_type_definition::InputObjectTypeDefinition, enum_type_definition::EnumTypeDefinition, custom_scalar_type_definition::CustomScalarTypeDefinition, scalar::Scalar};
|
3
|
+
use crate::helpers::{WrappedStruct, public_name, WrappedDefinition};
|
4
|
+
use bluejay_parser::ast::{TypeReference as ParserTypeReference};
|
5
|
+
use bluejay_core::{NamedTypeReference, ListTypeReference};
|
6
|
+
use bluejay_core::definition::{InputTypeReference as CoreInputTypeReference};
|
7
|
+
|
8
|
+
#[derive(Clone, Debug)]
|
9
|
+
pub enum BaseInputTypeReference {
|
10
|
+
BuiltinScalarType(bluejay_core::BuiltinScalarDefinition),
|
11
|
+
InputObjectType(WrappedDefinition<InputObjectTypeDefinition>),
|
12
|
+
EnumType(WrappedDefinition<EnumTypeDefinition>),
|
13
|
+
CustomScalarType(WrappedDefinition<CustomScalarTypeDefinition>),
|
14
|
+
}
|
15
|
+
|
16
|
+
impl bluejay_core::definition::AbstractBaseInputTypeReference for BaseInputTypeReference {
|
17
|
+
type CustomScalarTypeDefinition = CustomScalarTypeDefinition;
|
18
|
+
type InputObjectTypeDefinition = InputObjectTypeDefinition;
|
19
|
+
type EnumTypeDefinition = EnumTypeDefinition;
|
20
|
+
type WrappedCustomScalarTypeDefinition = WrappedStruct<CustomScalarTypeDefinition>;
|
21
|
+
type WrappedInputObjectTypeDefinition = WrappedStruct<InputObjectTypeDefinition>;
|
22
|
+
type WrappedEnumTypeDefinition = WrappedStruct<EnumTypeDefinition>;
|
23
|
+
|
24
|
+
fn to_concrete(&self) -> bluejay_core::definition::BaseInputTypeReferenceFromAbstract<Self> {
|
25
|
+
match self {
|
26
|
+
Self::CustomScalarType(cst) => bluejay_core::definition::BaseInputTypeReference::CustomScalarType(*cst.get(), Default::default()),
|
27
|
+
Self::InputObjectType(iotd) => bluejay_core::definition::BaseInputTypeReference::InputObjectType(*iotd.get(), Default::default()),
|
28
|
+
Self::BuiltinScalarType(bsd) => bluejay_core::definition::BaseInputTypeReference::BuiltinScalarType(*bsd),
|
29
|
+
Self::EnumType(etd) => bluejay_core::definition::BaseInputTypeReference::EnumType(*etd.get(), Default::default()),
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
impl BaseInputTypeReference {
|
35
|
+
pub fn new(value: Value) -> Result<Self, Error> {
|
36
|
+
if let Ok(wrapped_struct) = value.try_convert::<WrappedStruct<Scalar>>() {
|
37
|
+
Ok(Self::BuiltinScalarType(wrapped_struct.get().to_owned().into()))
|
38
|
+
} else if let Ok(wrapped_definition) = value.try_convert() {
|
39
|
+
Ok(Self::InputObjectType(wrapped_definition))
|
40
|
+
} else if let Ok(wrapped_definition) = value.try_convert() {
|
41
|
+
Ok(Self::EnumType(wrapped_definition))
|
42
|
+
} else if let Ok(wrapped_definition) = value.try_convert() {
|
43
|
+
Ok(Self::CustomScalarType(wrapped_definition))
|
44
|
+
} else {
|
45
|
+
Err(Error::new(
|
46
|
+
exception::type_error(),
|
47
|
+
format!(
|
48
|
+
"{} is not a valid input type",
|
49
|
+
value
|
50
|
+
),
|
51
|
+
))
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
pub fn mark(&self) {
|
56
|
+
match self {
|
57
|
+
Self::BuiltinScalarType(_) => {},
|
58
|
+
Self::InputObjectType(wd) => wd.mark(),
|
59
|
+
Self::EnumType(wd) => wd.mark(),
|
60
|
+
Self::CustomScalarType(wd) => wd.mark(),
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
fn sorbet_type(&self) -> String {
|
65
|
+
match self {
|
66
|
+
Self::BuiltinScalarType(bstd) => Scalar::from(*bstd).sorbet_type_fully_qualified_name().to_owned(),
|
67
|
+
Self::CustomScalarType(_) => "T.untyped".to_string(),
|
68
|
+
Self::EnumType(_) => "String".to_string(),
|
69
|
+
Self::InputObjectType(iotd) => iotd.fully_qualified_name(),
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
fn coerce_string(value: Value, path: &[String]) -> Result<Value, Vec<CoercionError>> {
|
74
|
+
if RString::from_value(value).is_some() {
|
75
|
+
Ok(value)
|
76
|
+
} else {
|
77
|
+
Err(vec![CoercionError::new(
|
78
|
+
format!("No implicit conversion of {} to String", public_name(value)),
|
79
|
+
path.to_owned(),
|
80
|
+
)])
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
fn coerce_integer(value: Value, path: &[String]) -> Result<Value, Vec<CoercionError>> {
|
85
|
+
if let Some(int_value) = Integer::from_value(value) {
|
86
|
+
int_value
|
87
|
+
.to_i32()
|
88
|
+
.map(|_| value)
|
89
|
+
.map_err(|_| vec![CoercionError::new(
|
90
|
+
"Integer values must fit within 32 bits signed".to_owned(),
|
91
|
+
path.to_owned(),
|
92
|
+
)])
|
93
|
+
} else {
|
94
|
+
Err(vec![CoercionError::new(
|
95
|
+
format!("No implicit conversion of {} to integer", public_name(value)),
|
96
|
+
path.to_owned(),
|
97
|
+
)])
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
fn coerce_float(value: Value, path: &[String]) -> Result<Value, Vec<CoercionError>> {
|
102
|
+
if Float::from_value(value).is_some() {
|
103
|
+
Ok(value)
|
104
|
+
} else if Integer::from_value(value).is_some() {
|
105
|
+
Self::coerce_integer(value, path)
|
106
|
+
} else {
|
107
|
+
Err(vec![CoercionError::new(
|
108
|
+
format!("No implicit conversion of {} to Float", public_name(value)),
|
109
|
+
path.to_owned(),
|
110
|
+
)])
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
fn coerce_boolean(value: Value, path: &[String]) -> Result<Value, Vec<CoercionError>> {
|
115
|
+
if value.is_kind_of(magnus::class::true_class()) || value.is_kind_of(magnus::class::false_class()) {
|
116
|
+
Ok(value)
|
117
|
+
} else {
|
118
|
+
Err(vec![CoercionError::new(
|
119
|
+
format!("No implicit conversion of {} to Boolean", public_name(value)),
|
120
|
+
path.to_owned(),
|
121
|
+
)])
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
fn coerce_id(value: Value, path: &[String]) -> Result<Value, Vec<CoercionError>> {
|
126
|
+
if RString::from_value(value).is_some() {
|
127
|
+
Ok(value)
|
128
|
+
} else if Integer::from_value(value).is_some() {
|
129
|
+
Self::coerce_integer(value, path)
|
130
|
+
} else {
|
131
|
+
Err(vec![CoercionError::new(
|
132
|
+
format!("No implicit conversion of {} to ID", public_name(value)),
|
133
|
+
path.to_owned(),
|
134
|
+
)])
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
impl CoerceInput for bluejay_core::BuiltinScalarDefinition {
|
140
|
+
fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
|
141
|
+
Ok(match self {
|
142
|
+
Self::String => BaseInputTypeReference::coerce_string(value, path),
|
143
|
+
Self::Int => BaseInputTypeReference::coerce_integer(value, path),
|
144
|
+
Self::Float => BaseInputTypeReference::coerce_float(value, path),
|
145
|
+
Self::Boolean => BaseInputTypeReference::coerce_boolean(value, path),
|
146
|
+
Self::ID => BaseInputTypeReference::coerce_id(value, path),
|
147
|
+
})
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
impl CoerceInput for BaseInputTypeReference {
|
152
|
+
fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
|
153
|
+
match self {
|
154
|
+
Self::BuiltinScalarType(bstd) => bstd.coerce_input(value, path),
|
155
|
+
Self::InputObjectType(wrapped_definition) => {
|
156
|
+
wrapped_definition.as_ref().coerce_input(value, path)
|
157
|
+
},
|
158
|
+
Self::EnumType(wrapped_definition) => {
|
159
|
+
wrapped_definition.as_ref().coerce_input(value, path)
|
160
|
+
},
|
161
|
+
Self::CustomScalarType(wrapped_definition) => {
|
162
|
+
wrapped_definition.as_ref().coerce_input(value, path)
|
163
|
+
},
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
#[derive(Clone, Debug, TypedData)]
|
169
|
+
#[magnus(class = "Bluejay::InputTypeReference", mark)]
|
170
|
+
#[repr(transparent)]
|
171
|
+
pub struct InputTypeReference(CoreInputTypeReference<BaseInputTypeReference, WrappedInputTypeReference>);
|
172
|
+
|
173
|
+
impl AsRef<CoreInputTypeReference<BaseInputTypeReference, WrappedInputTypeReference>> for InputTypeReference {
|
174
|
+
fn as_ref(&self) -> &CoreInputTypeReference<BaseInputTypeReference, WrappedInputTypeReference> {
|
175
|
+
&self.0
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
impl bluejay_core::definition::AbstractInputTypeReference for InputTypeReference {
|
180
|
+
type BaseInputTypeReference = BaseInputTypeReference;
|
181
|
+
type Wrapper = WrappedInputTypeReference;
|
182
|
+
}
|
183
|
+
|
184
|
+
impl DataTypeFunctions for InputTypeReference {
|
185
|
+
fn mark(&self) {
|
186
|
+
match &self.0 {
|
187
|
+
CoreInputTypeReference::List(inner, _) => inner.mark(),
|
188
|
+
CoreInputTypeReference::Base(inner, _) => inner.mark(),
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
#[derive(Clone, Debug)]
|
194
|
+
#[repr(transparent)]
|
195
|
+
pub struct WrappedInputTypeReference(WrappedStruct<InputTypeReference>);
|
196
|
+
|
197
|
+
impl AsRef<CoreInputTypeReference<BaseInputTypeReference, Self>> for WrappedInputTypeReference {
|
198
|
+
fn as_ref(&self) -> &CoreInputTypeReference<BaseInputTypeReference, Self> {
|
199
|
+
self.0.get().as_ref()
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
impl WrappedInputTypeReference {
|
204
|
+
fn mark(&self) {
|
205
|
+
self.0.mark()
|
206
|
+
}
|
207
|
+
|
208
|
+
fn get(&self) -> &InputTypeReference {
|
209
|
+
self.0.get()
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
impl From<WrappedStruct<InputTypeReference>> for WrappedInputTypeReference {
|
214
|
+
fn from(value: WrappedStruct<InputTypeReference>) -> Self {
|
215
|
+
Self(value)
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
impl From<InputTypeReference> for WrappedInputTypeReference {
|
220
|
+
fn from(value: InputTypeReference) -> Self {
|
221
|
+
Self(WrappedStruct::wrap(value))
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
impl AsRef<WrappedStruct<InputTypeReference>> for WrappedInputTypeReference {
|
226
|
+
fn as_ref(&self) -> &WrappedStruct<InputTypeReference> {
|
227
|
+
&self.0
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
impl InputTypeReference {
|
232
|
+
pub fn new(kw: RHash) -> Result<Self, Error> {
|
233
|
+
let args = get_kwargs(kw, &["type", "required"], &[])?;
|
234
|
+
let (r#type, required): (Value, bool) = args.required;
|
235
|
+
let _: () = args.optional;
|
236
|
+
let _: () = args.splat;
|
237
|
+
let base = BaseInputTypeReference::new(r#type)?;
|
238
|
+
Ok(Self(CoreInputTypeReference::Base(base, required)))
|
239
|
+
}
|
240
|
+
|
241
|
+
pub fn list(kw: RHash) -> Result<Self, Error> {
|
242
|
+
let args = get_kwargs(kw, &["type", "required"], &[])?;
|
243
|
+
let (r#type, required): (WrappedStruct<Self>, bool) = args.required;
|
244
|
+
let _: () = args.optional;
|
245
|
+
let _: () = args.splat;
|
246
|
+
Ok(Self(CoreInputTypeReference::List(r#type.into(), required)))
|
247
|
+
}
|
248
|
+
|
249
|
+
fn coerce_required<F: Fn(Value, &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error>>(value: Value, required: bool, path: &[String], f: F) -> Result<Result<Value, Vec<CoercionError>>, Error> {
|
250
|
+
if required && value.is_nil() {
|
251
|
+
Ok(Err(vec![CoercionError::new(
|
252
|
+
"Got null when a non-null value was expected".to_owned(),
|
253
|
+
path.to_owned(),
|
254
|
+
)]))
|
255
|
+
} else if value.is_nil() {
|
256
|
+
Ok(Ok(value))
|
257
|
+
} else {
|
258
|
+
f(value, path)
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
pub fn from_parser_type_reference(parser_type_reference: &ParserTypeReference, base: BaseInputTypeReference) -> Self {
|
263
|
+
match parser_type_reference {
|
264
|
+
ParserTypeReference::NamedType(ntr) => Self(CoreInputTypeReference::Base(base, ntr.required())),
|
265
|
+
ParserTypeReference::ListType(ltr) => Self(CoreInputTypeReference::List(Self::from_parser_type_reference(ltr.inner(), base).into(), ltr.required())),
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
fn is_list(&self) -> bool {
|
270
|
+
matches!(&self.0, CoreInputTypeReference::List(_, _))
|
271
|
+
}
|
272
|
+
|
273
|
+
fn is_base(&self) -> bool {
|
274
|
+
matches!(&self.0, CoreInputTypeReference::Base(_, _))
|
275
|
+
}
|
276
|
+
|
277
|
+
pub fn is_required(&self) -> bool {
|
278
|
+
self.0.is_required()
|
279
|
+
}
|
280
|
+
|
281
|
+
fn unwrap_list(&self) -> Result<WrappedStruct<InputTypeReference>, Error> {
|
282
|
+
match &self.0 {
|
283
|
+
CoreInputTypeReference::List(inner, _) => Ok(*inner.as_ref()),
|
284
|
+
CoreInputTypeReference::Base(_, _) => Err(Error::new(
|
285
|
+
exception::runtime_error(),
|
286
|
+
"Tried to unwrap a non-list InputTypeReference".to_owned(),
|
287
|
+
)),
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
fn sorbet_type(&self) -> String {
|
292
|
+
let is_required = self.0.is_required();
|
293
|
+
let inner = match &self.0 {
|
294
|
+
CoreInputTypeReference::List(inner, _) => format!("T::Array[{}]", inner.get().sorbet_type()),
|
295
|
+
CoreInputTypeReference::Base(base, _) => base.sorbet_type(),
|
296
|
+
};
|
297
|
+
if is_required {
|
298
|
+
inner
|
299
|
+
} else {
|
300
|
+
format!("T.nilable({})", inner)
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}
|
304
|
+
|
305
|
+
impl CoerceInput for InputTypeReference {
|
306
|
+
fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
|
307
|
+
match &self.0 {
|
308
|
+
CoreInputTypeReference::Base(inner, required) => {
|
309
|
+
Self::coerce_required(value, *required, path, |value, path| inner.coerce_input(value, path))
|
310
|
+
},
|
311
|
+
CoreInputTypeReference::List(inner, required) => {
|
312
|
+
Self::coerce_required(value, *required, path, |value, path| {
|
313
|
+
let inner = inner.get();
|
314
|
+
|
315
|
+
if let Some(array) = RArray::from_value(value) {
|
316
|
+
let coerced = RArray::new();
|
317
|
+
let mut errors = Vec::new();
|
318
|
+
|
319
|
+
unsafe {
|
320
|
+
for (idx, value) in array.as_slice().iter().enumerate() {
|
321
|
+
let mut path = path.to_owned();
|
322
|
+
path.push(idx.to_string());
|
323
|
+
|
324
|
+
match inner.coerce_input(*value, &path)? {
|
325
|
+
Ok(coerced_value) => { coerced.push(coerced_value).unwrap(); },
|
326
|
+
Err(errs) => { errors.extend(errs); },
|
327
|
+
}
|
328
|
+
}
|
329
|
+
}
|
330
|
+
|
331
|
+
Ok(if errors.is_empty() {
|
332
|
+
Ok(*coerced)
|
333
|
+
} else {
|
334
|
+
Err(errors)
|
335
|
+
})
|
336
|
+
} else {
|
337
|
+
let inner_result = inner.coerce_input(value, path)?;
|
338
|
+
Ok(inner_result.map(|coerced_value| *RArray::from_slice(&[coerced_value])))
|
339
|
+
}
|
340
|
+
})
|
341
|
+
}
|
342
|
+
}
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
pub fn init() -> Result<(), Error> {
|
347
|
+
let class = root().define_class("InputTypeReference", Default::default())?;
|
348
|
+
|
349
|
+
class.define_singleton_method("new", function!(InputTypeReference::new, 1))?;
|
350
|
+
class.define_singleton_method("list", function!(InputTypeReference::list, 1))?;
|
351
|
+
class.define_method("list?", method!(InputTypeReference::is_list, 0))?;
|
352
|
+
class.define_method("base?", method!(InputTypeReference::is_base, 0))?;
|
353
|
+
class.define_method("required?", method!(InputTypeReference::is_required, 0))?;
|
354
|
+
class.define_method("sorbet_type", method!(InputTypeReference::sorbet_type, 0))?;
|
355
|
+
class.define_method("unwrap_list", method!(InputTypeReference::unwrap_list, 0))?;
|
356
|
+
|
357
|
+
Ok(())
|
358
|
+
}
|