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.
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
+ }