bluejay 0.1.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +423 -0
  3. data/Cargo.toml +2 -0
  4. data/LICENSE +21 -0
  5. data/README.md +33 -0
  6. data/ext/Cargo.toml +17 -0
  7. data/ext/extconf.rb +6 -0
  8. data/ext/src/execution/coerce_result.rs +54 -0
  9. data/ext/src/execution/engine.rs +466 -0
  10. data/ext/src/execution/execution_error.rs +28 -0
  11. data/ext/src/execution/field_error.rs +8 -0
  12. data/ext/src/execution/key_store.rs +23 -0
  13. data/ext/src/execution.rs +10 -0
  14. data/ext/src/helpers/public_name.rs +21 -0
  15. data/ext/src/helpers/typed_frozen_r_array.rs +56 -0
  16. data/ext/src/helpers/wrapped_definition.rs +79 -0
  17. data/ext/src/helpers/wrapped_struct.rs +97 -0
  18. data/ext/src/helpers.rs +8 -0
  19. data/ext/src/lib.rs +10 -0
  20. data/ext/src/ruby_api/arguments_definition.rs +8 -0
  21. data/ext/src/ruby_api/coerce_input.rs +6 -0
  22. data/ext/src/ruby_api/coercion_error.rs +61 -0
  23. data/ext/src/ruby_api/custom_scalar_type_definition.rs +62 -0
  24. data/ext/src/ruby_api/enum_type_definition.rs +107 -0
  25. data/ext/src/ruby_api/enum_value_definition.rs +58 -0
  26. data/ext/src/ruby_api/enum_value_definitions.rs +8 -0
  27. data/ext/src/ruby_api/execution_error.rs +48 -0
  28. data/ext/src/ruby_api/execution_result.rs +40 -0
  29. data/ext/src/ruby_api/field_definition.rs +112 -0
  30. data/ext/src/ruby_api/fields_definition.rs +8 -0
  31. data/ext/src/ruby_api/input_fields_definition.rs +8 -0
  32. data/ext/src/ruby_api/input_object_type_definition.rs +138 -0
  33. data/ext/src/ruby_api/input_type_reference.rs +358 -0
  34. data/ext/src/ruby_api/input_value_definition.rs +98 -0
  35. data/ext/src/ruby_api/interface_implementation.rs +42 -0
  36. data/ext/src/ruby_api/interface_implementations.rs +8 -0
  37. data/ext/src/ruby_api/interface_type_definition.rs +82 -0
  38. data/ext/src/ruby_api/json_value.rs +111 -0
  39. data/ext/src/ruby_api/object_type_definition.rs +100 -0
  40. data/ext/src/ruby_api/output_type_reference.rs +238 -0
  41. data/ext/src/ruby_api/r_result.rs +84 -0
  42. data/ext/src/ruby_api/scalar.rs +45 -0
  43. data/ext/src/ruby_api/schema_definition.rs +270 -0
  44. data/ext/src/ruby_api/union_member_type.rs +41 -0
  45. data/ext/src/ruby_api/union_member_types.rs +8 -0
  46. data/ext/src/ruby_api/union_type_definition.rs +89 -0
  47. data/ext/src/ruby_api/validation_error.rs +63 -0
  48. data/ext/src/ruby_api.rs +75 -0
  49. data/lib/bluejay/base_input_type_reference.rb +13 -0
  50. data/lib/bluejay/base_output_type_reference.rb +15 -0
  51. data/lib/bluejay/custom_scalar_type.rb +40 -0
  52. data/lib/bluejay/enum_type.rb +44 -0
  53. data/lib/bluejay/ext.bundle +0 -0
  54. data/lib/bluejay/finalize.rb +27 -0
  55. data/lib/bluejay/input_type.rb +64 -0
  56. data/lib/bluejay/input_type_reference_shorthands.rb +28 -0
  57. data/lib/bluejay/interface_type.rb +60 -0
  58. data/lib/bluejay/json_value.rb +16 -0
  59. data/lib/bluejay/name_from_class.rb +18 -0
  60. data/lib/bluejay/object_type.rb +68 -0
  61. data/lib/bluejay/output_type_reference_shorthands.rb +28 -0
  62. data/lib/bluejay/schema.rb +63 -0
  63. data/lib/bluejay/union_type.rb +43 -0
  64. data/lib/bluejay/version.rb +5 -0
  65. data/lib/bluejay.rb +28 -0
  66. data/lib/rbi_ext/model.rb +64 -0
  67. data/lib/tapioca/dsl/compilers/input_type.rb +34 -0
  68. data/lib/tapioca/dsl/compilers/interface_type.rb +29 -0
  69. data/lib/tapioca/dsl/compilers/object_type.rb +43 -0
  70. data/lib/tapioca/dsl/compilers/schema.rb +42 -0
  71. 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
+ }