bluejay 0.1.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|