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,79 @@
1
+ use once_cell::unsync::OnceCell;
2
+ use magnus::{RClass, TypedData, Error, Module, exception, gc, TryConvert, Value};
3
+ use super::WrappedStruct;
4
+
5
+ pub trait HasDefinitionWrapper: TypedData {
6
+ fn wrapping_class() -> RClass;
7
+ }
8
+
9
+ #[derive(Debug)]
10
+ pub struct WrappedDefinition<T: HasDefinitionWrapper> {
11
+ cls: RClass,
12
+ memoized_definition: OnceCell<WrappedStruct<T>>,
13
+ }
14
+
15
+ impl<T: HasDefinitionWrapper> Clone for WrappedDefinition<T> {
16
+ fn clone(&self) -> Self {
17
+ Self { cls: self.cls.clone(), memoized_definition: self.memoized_definition.clone() }
18
+ }
19
+ }
20
+
21
+ impl<T: HasDefinitionWrapper> WrappedDefinition<T> {
22
+ pub fn new(cls: RClass) -> Result<Self, Error> {
23
+ if cls.is_inherited(T::wrapping_class()) {
24
+ Ok(Self { cls, memoized_definition: OnceCell::new() })
25
+ } else {
26
+ Err(Error::new(
27
+ exception::type_error(),
28
+ format!(
29
+ "no implicit conversion of {} into {}",
30
+ cls,
31
+ T::wrapping_class()
32
+ ),
33
+ ))
34
+ }
35
+ }
36
+
37
+ pub fn get(&self) -> &WrappedStruct<T> {
38
+ self.memoized_definition
39
+ .get_or_init(|| self.cls.funcall("definition", ()).unwrap())
40
+ }
41
+
42
+ pub fn mark(&self) {
43
+ gc::mark(self.cls);
44
+ if let Some(ws) = self.memoized_definition.get() {
45
+ ws.mark();
46
+ }
47
+ }
48
+
49
+ pub fn fully_qualified_name(&self) -> String {
50
+ unsafe { self.cls.name() }.into_owned()
51
+ }
52
+
53
+ pub fn class(&self) -> RClass {
54
+ self.cls
55
+ }
56
+ }
57
+
58
+ impl<T: HasDefinitionWrapper> TryConvert for WrappedDefinition<T> {
59
+ fn try_convert(val: Value) -> Result<Self, Error> {
60
+ let cls = RClass::from_value(val).ok_or_else(|| {
61
+ Error::new(
62
+ exception::type_error(),
63
+ format!(
64
+ "no implicit conversion of {} into {}",
65
+ unsafe { val.classname() },
66
+ T::wrapping_class()
67
+ ),
68
+ )
69
+ })?;
70
+
71
+ Self::new(cls)
72
+ }
73
+ }
74
+
75
+ impl<T: HasDefinitionWrapper> AsRef<T> for WrappedDefinition<T> {
76
+ fn as_ref(&self) -> &T {
77
+ self.get().as_ref()
78
+ }
79
+ }
@@ -0,0 +1,97 @@
1
+ use magnus::{error::Error, exception, gc, value::Value, RTypedData, TryConvert, TypedData};
2
+ use std::{marker::PhantomData, ops::Deref};
3
+
4
+ /// A small wrapper for `RTypedData` that keeps track of the concrete struct
5
+ /// type, and the underlying [`Value`] for GC purposes.
6
+ #[derive(Debug)]
7
+ #[repr(transparent)]
8
+ pub struct WrappedStruct<T: TypedData> {
9
+ inner: RTypedData,
10
+ phantom: PhantomData<T>,
11
+ }
12
+
13
+ impl<T: TypedData> Copy for WrappedStruct<T> {}
14
+
15
+ impl<T: TypedData> Clone for WrappedStruct<T> {
16
+ fn clone(&self) -> Self {
17
+ *self
18
+ }
19
+ }
20
+
21
+ impl<T: TypedData> WrappedStruct<T> {
22
+ /// Gets the underlying struct.
23
+ pub fn get(&self) -> &T {
24
+ self.inner.get().unwrap()
25
+ }
26
+
27
+ /// Get the Ruby [`Value`] for this struct.
28
+ pub fn to_value(&self) -> Value {
29
+ self.inner.into()
30
+ }
31
+
32
+ /// Marks the Ruby [`Value`] for GC.
33
+ pub fn mark(&self) {
34
+ gc::mark(&self.inner.into());
35
+ }
36
+
37
+ pub fn wrap(data: T) -> Self {
38
+ let inner = RTypedData::wrap(data);
39
+ Self {
40
+ inner,
41
+ phantom: PhantomData,
42
+ }
43
+ }
44
+ }
45
+
46
+ impl<T: TypedData> From<WrappedStruct<T>> for Value {
47
+ fn from(wrapped_struct: WrappedStruct<T>) -> Self {
48
+ wrapped_struct.to_value()
49
+ }
50
+ }
51
+
52
+ impl<T: TypedData> Deref for WrappedStruct<T> {
53
+ type Target = RTypedData;
54
+
55
+ fn deref(&self) -> &Self::Target {
56
+ &self.inner
57
+ }
58
+ }
59
+
60
+ impl<T: TypedData> From<T> for WrappedStruct<T> {
61
+ fn from(t: T) -> Self {
62
+ Self {
63
+ inner: RTypedData::wrap(t).into(),
64
+ phantom: PhantomData,
65
+ }
66
+ }
67
+ }
68
+
69
+ impl<T> TryConvert for WrappedStruct<T>
70
+ where
71
+ T: TypedData,
72
+ {
73
+ fn try_convert(val: Value) -> Result<Self, Error> {
74
+ let inner = RTypedData::from_value(val).ok_or_else(|| {
75
+ Error::new(
76
+ exception::type_error(),
77
+ format!(
78
+ "no implicit conversion of {} into {}",
79
+ unsafe { val.classname() },
80
+ T::class()
81
+ ),
82
+ )
83
+ })?;
84
+ inner.get::<T>()?;
85
+
86
+ Ok(Self {
87
+ inner,
88
+ phantom: PhantomData,
89
+ })
90
+ }
91
+ }
92
+
93
+ impl<T: TypedData> AsRef<T> for WrappedStruct<T> {
94
+ fn as_ref(&self) -> &T {
95
+ self.get()
96
+ }
97
+ }
@@ -0,0 +1,8 @@
1
+ mod wrapped_definition;
2
+ pub use wrapped_definition::{HasDefinitionWrapper, WrappedDefinition};
3
+ mod public_name;
4
+ pub use public_name::public_name;
5
+ mod typed_frozen_r_array;
6
+ pub use typed_frozen_r_array::TypedFrozenRArray;
7
+ mod wrapped_struct;
8
+ pub use wrapped_struct::WrappedStruct;
data/ext/src/lib.rs ADDED
@@ -0,0 +1,10 @@
1
+ use magnus::{Error};
2
+
3
+ mod execution;
4
+ mod helpers;
5
+ mod ruby_api;
6
+
7
+ #[magnus::init]
8
+ fn init() -> Result<(), Error> {
9
+ ruby_api::init()
10
+ }
@@ -0,0 +1,8 @@
1
+ use super::{input_value_definition::InputValueDefinition};
2
+ use crate::helpers::{WrappedStruct, TypedFrozenRArray};
3
+
4
+ pub type ArgumentsDefinition = TypedFrozenRArray<WrappedStruct<InputValueDefinition>>;
5
+
6
+ impl bluejay_core::definition::ArgumentsDefinition for ArgumentsDefinition {
7
+ type ArgumentDefinition = InputValueDefinition;
8
+ }
@@ -0,0 +1,6 @@
1
+ use super::coercion_error::CoercionError;
2
+ use magnus::{Value, Error};
3
+
4
+ pub trait CoerceInput {
5
+ fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error>;
6
+ }
@@ -0,0 +1,61 @@
1
+ use super::{root, ExecutionError};
2
+ use crate::helpers::WrappedStruct;
3
+ use magnus::{function, Error, Module, Object, RArray, Value, method, rb_sys::AsRawValue};
4
+
5
+ #[derive(Clone, Debug, PartialEq)]
6
+ #[magnus::wrap(class = "Bluejay::CoercionError", mark)]
7
+ pub struct CoercionError {
8
+ message: String,
9
+ path: Vec<String>,
10
+ }
11
+
12
+ impl CoercionError {
13
+ pub fn new(message: String, path: Vec<String>) -> Self {
14
+ Self { message, path }
15
+ }
16
+
17
+ pub fn message(&self) -> &str {
18
+ self.message.as_str()
19
+ }
20
+
21
+ pub fn path(&self) -> RArray {
22
+ RArray::from_iter(self.path.iter().map(|s| s.as_str()))
23
+ }
24
+
25
+ pub fn eql(&self, other: Value) -> bool {
26
+ if let Ok(other) = other.try_convert::<&Self>() {
27
+ self == other
28
+ } else {
29
+ false
30
+ }
31
+ }
32
+
33
+ fn inspect(rb_self: WrappedStruct<Self>) -> Result<String, Error> {
34
+ let rs_self = rb_self.get();
35
+
36
+ Ok(format!(
37
+ "#<Bluejay::CoercionError:0x{:016x} @message={:?} @path={:?}>",
38
+ rb_self.to_value().as_raw(),
39
+ rs_self.message,
40
+ rs_self.path,
41
+ ))
42
+ }
43
+ }
44
+
45
+ impl Into<ExecutionError> for CoercionError {
46
+ fn into(self) -> ExecutionError {
47
+ ExecutionError::new(self.message)
48
+ }
49
+ }
50
+
51
+ pub fn init() -> Result<(), Error> {
52
+ let class = root().define_class("CoercionError", Default::default())?;
53
+
54
+ class.define_singleton_method("new", function!(CoercionError::new, 2))?;
55
+ class.define_method("message", method!(CoercionError::message, 0))?;
56
+ class.define_method("path", method!(CoercionError::path, 0))?;
57
+ class.define_method("==", method!(CoercionError::eql, 1))?;
58
+ class.define_method("inspect", method!(CoercionError::inspect, 0))?;
59
+
60
+ Ok(())
61
+ }
@@ -0,0 +1,62 @@
1
+ use magnus::{function, Error, Module, Object, Value, scan_args::get_kwargs, RHash, memoize, TypedData, DataTypeFunctions, RClass};
2
+ use super::{root, coerce_input::CoerceInput, coercion_error::CoercionError};
3
+ use crate::helpers::{HasDefinitionWrapper};
4
+
5
+ #[derive(Clone, Debug, TypedData)]
6
+ #[magnus(class = "Bluejay::CustomScalarTypeDefinition", mark)]
7
+ pub struct CustomScalarTypeDefinition {
8
+ name: String,
9
+ description: Option<String>,
10
+ }
11
+
12
+ // TODO: add ability to coerce input and possibly coerce result
13
+
14
+ impl CustomScalarTypeDefinition {
15
+ fn new(kw: RHash) -> Result<Self, Error> {
16
+ let args = get_kwargs(kw, &["name", "description"], &[])?;
17
+ let (name, description): (String, Option<String>) = args.required;
18
+ let _: () = args.optional;
19
+ let _: () = args.splat;
20
+ Ok(Self { name, description })
21
+ }
22
+
23
+ pub fn name(&self) -> &str {
24
+ self.name.as_str()
25
+ }
26
+
27
+ pub fn description(&self) -> Option<&str> {
28
+ self.description.as_ref().map(String::as_str)
29
+ }
30
+ }
31
+
32
+ impl DataTypeFunctions for CustomScalarTypeDefinition {}
33
+
34
+ impl HasDefinitionWrapper for CustomScalarTypeDefinition {
35
+ fn wrapping_class() -> RClass {
36
+ *memoize!(RClass: root().define_class("CustomScalarType", Default::default()).unwrap())
37
+ }
38
+ }
39
+
40
+ impl CoerceInput for CustomScalarTypeDefinition {
41
+ fn coerce_input(&self, value: Value, _path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
42
+ Ok(Ok(value))
43
+ }
44
+ }
45
+
46
+ impl bluejay_core::definition::ScalarTypeDefinition for CustomScalarTypeDefinition {
47
+ fn description(&self) -> Option<&str> {
48
+ self.description.as_ref().map(|s| s.as_str())
49
+ }
50
+
51
+ fn name(&self) -> &str {
52
+ self.name.as_str()
53
+ }
54
+ }
55
+
56
+ pub fn init() -> Result<(), Error> {
57
+ let class = root().define_class("CustomScalarTypeDefinition", Default::default())?;
58
+
59
+ class.define_singleton_method("new", function!(CustomScalarTypeDefinition::new, 1))?;
60
+
61
+ Ok(())
62
+ }
@@ -0,0 +1,107 @@
1
+ use magnus::{function, Error, Module, Object, scan_args::get_kwargs, RHash, Value, memoize, TypedData, RArray, DataTypeFunctions, RClass, gc};
2
+ use bluejay_core::AsIter;
3
+ use super::{root, enum_value_definitions::EnumValueDefinitions, coerce_input::CoerceInput, coercion_error::CoercionError};
4
+ use crate::helpers::{public_name, HasDefinitionWrapper};
5
+ use crate::execution::{FieldError, CoerceResult};
6
+
7
+ #[derive(Debug, TypedData)]
8
+ #[magnus(class = "Bluejay::EnumTypeDefinition", mark)]
9
+ pub struct EnumTypeDefinition {
10
+ name: String,
11
+ description: Option<String>,
12
+ enum_value_definitions: EnumValueDefinitions,
13
+ }
14
+
15
+ impl EnumTypeDefinition {
16
+ fn new(kw: RHash) -> Result<Self, Error> {
17
+ let args = get_kwargs(kw, &["name", "enum_value_definitions", "description"], &[])?;
18
+ let (name, enum_value_definitions, description): (String, RArray, Option<String>) = args.required;
19
+ let _: () = args.optional;
20
+ let _: () = args.splat;
21
+ let enum_value_definitions = EnumValueDefinitions::new(enum_value_definitions)?;
22
+ Ok(Self { name, description, enum_value_definitions })
23
+ }
24
+
25
+ pub fn name(&self) -> &str {
26
+ self.name.as_str()
27
+ }
28
+
29
+ pub fn description(&self) -> Option<&str> {
30
+ self.description.as_ref().map(String::as_str)
31
+ }
32
+
33
+ pub fn enum_value_definitions(&self) -> &EnumValueDefinitions {
34
+ &self.enum_value_definitions
35
+ }
36
+ }
37
+
38
+ impl DataTypeFunctions for EnumTypeDefinition {
39
+ fn mark(&self) {
40
+ gc::mark(self.enum_value_definitions);
41
+ }
42
+ }
43
+
44
+ impl CoerceInput for EnumTypeDefinition {
45
+ fn coerce_input(&self, value: Value, path: &[String]) -> Result<Result<Value, Vec<CoercionError>>, Error> {
46
+ let s: Result<String, _> = value.try_convert();
47
+ match s {
48
+ Ok(s) => {
49
+ // TODO: don't use const_get
50
+ if self.enum_value_definitions.iter().any(|evd| evd.name() == s.as_str()) {
51
+ Ok(Ok(value))
52
+ } else {
53
+ Ok(Err(vec![CoercionError::new(
54
+ format!("No member `{}` on {}", s.as_str(), self.name.as_str()),
55
+ path.to_owned(),
56
+ )]))
57
+ }
58
+ },
59
+ Err(_) => {
60
+ Ok(Err(vec![CoercionError::new(
61
+ format!("No implicit conversion of {} to {}", public_name(value), self.name.as_str()),
62
+ path.to_owned(),
63
+ )]))
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ impl HasDefinitionWrapper for EnumTypeDefinition {
70
+ fn wrapping_class() -> RClass {
71
+ *memoize!(RClass: root().define_class("EnumType", Default::default()).unwrap())
72
+ }
73
+ }
74
+
75
+ impl bluejay_core::definition::EnumTypeDefinition for EnumTypeDefinition {
76
+ type EnumValueDefinitions = EnumValueDefinitions;
77
+
78
+ fn description(&self) -> Option<&str> {
79
+ self.description.as_ref().map(String::as_str)
80
+ }
81
+
82
+ fn name(&self) -> &str {
83
+ self.name.as_str()
84
+ }
85
+
86
+ fn enum_value_definitions(&self) -> &Self::EnumValueDefinitions {
87
+ &self.enum_value_definitions
88
+ }
89
+ }
90
+
91
+ impl CoerceResult for EnumTypeDefinition {
92
+ fn coerce_result(&self, value: Value) -> Result<Value, FieldError> {
93
+ if value.try_convert().ok().map(|value: String| self.enum_value_definitions.iter().any(|evd| evd.name() == value.as_str())).unwrap_or(false) {
94
+ Ok(value)
95
+ } else {
96
+ Err(FieldError::CannotCoerceResultToEnumType)
97
+ }
98
+ }
99
+ }
100
+
101
+ pub fn init() -> Result<(), Error> {
102
+ let class = root().define_class("EnumTypeDefinition", Default::default())?;
103
+
104
+ class.define_singleton_method("new", function!(EnumTypeDefinition::new, 1))?;
105
+
106
+ Ok(())
107
+ }
@@ -0,0 +1,58 @@
1
+ use super::root;
2
+ use crate::helpers::WrappedStruct;
3
+ use magnus::{function, Error, Module, Object, scan_args::get_kwargs, RHash, TypedData, DataTypeFunctions, method};
4
+
5
+ #[derive(Clone, Debug, TypedData)]
6
+ #[magnus(class = "Bluejay::EnumValueDefinition", mark)]
7
+ pub struct EnumValueDefinition {
8
+ name: String,
9
+ description: Option<String>,
10
+ }
11
+
12
+ impl EnumValueDefinition {
13
+ pub fn new(kw: RHash) -> Result<Self, Error> {
14
+ let args = get_kwargs(kw, &["name"], &["description"])?;
15
+ let (name,): (String,) = args.required;
16
+ let (description,): (Option<String>,) = args.optional;
17
+ let _: () = args.splat;
18
+ Ok(Self { name, description })
19
+ }
20
+
21
+ pub fn name(&self) -> &str {
22
+ self.name.as_str()
23
+ }
24
+ }
25
+
26
+ impl DataTypeFunctions for EnumValueDefinition {
27
+ fn mark(&self) {
28
+ }
29
+ }
30
+
31
+ impl bluejay_core::definition::EnumValueDefinition for EnumValueDefinition {
32
+ fn description(&self) -> Option<&str> {
33
+ self.description.as_ref().map(String::as_str)
34
+ }
35
+
36
+ fn name(&self) -> &str {
37
+ self.name.as_str()
38
+ }
39
+ }
40
+
41
+ impl bluejay_core::definition::EnumValueDefinition for WrappedStruct<EnumValueDefinition> {
42
+ fn description(&self) -> Option<&str> {
43
+ self.get().description()
44
+ }
45
+
46
+ fn name(&self) -> &str {
47
+ self.get().name()
48
+ }
49
+ }
50
+
51
+ pub fn init() -> Result<(), Error> {
52
+ let class = root().define_class("EnumValueDefinition", Default::default())?;
53
+
54
+ class.define_singleton_method("new", function!(EnumValueDefinition::new, 1))?;
55
+ class.define_method("name", method!(EnumValueDefinition::name, 0))?;
56
+
57
+ Ok(())
58
+ }
@@ -0,0 +1,8 @@
1
+ use super::{enum_value_definition::EnumValueDefinition};
2
+ use crate::helpers::{WrappedStruct, TypedFrozenRArray};
3
+
4
+ pub type EnumValueDefinitions = TypedFrozenRArray<WrappedStruct<EnumValueDefinition>>;
5
+
6
+ impl bluejay_core::definition::EnumValueDefinitions for EnumValueDefinitions {
7
+ type EnumValueDefinition = EnumValueDefinition;
8
+ }
@@ -0,0 +1,48 @@
1
+ use super::root;
2
+ use crate::helpers::WrappedStruct;
3
+ use magnus::{function, Error, Module, Object, Value, method, rb_sys::AsRawValue};
4
+
5
+ #[derive(Clone, Debug, PartialEq)]
6
+ #[magnus::wrap(class = "Bluejay::ExecutionError", mark)]
7
+ pub struct ExecutionError {
8
+ message: String,
9
+ }
10
+
11
+ impl ExecutionError {
12
+ pub fn new(message: String) -> Self {
13
+ Self { message }
14
+ }
15
+
16
+ pub fn message(&self) -> &str {
17
+ self.message.as_str()
18
+ }
19
+
20
+ pub fn eql(&self, other: Value) -> bool {
21
+ if let Ok(other) = other.try_convert::<&Self>() {
22
+ self == other
23
+ } else {
24
+ false
25
+ }
26
+ }
27
+
28
+ fn inspect(rb_self: WrappedStruct<Self>) -> Result<String, Error> {
29
+ let rs_self = rb_self.get();
30
+
31
+ Ok(format!(
32
+ "#<Bluejay::ExecutionError:0x{:016x} @message={:?}>",
33
+ rb_self.to_value().as_raw(),
34
+ rs_self.message,
35
+ ))
36
+ }
37
+ }
38
+
39
+ pub fn init() -> Result<(), Error> {
40
+ let class = root().define_class("ExecutionError", Default::default())?;
41
+
42
+ class.define_singleton_method("new", function!(ExecutionError::new, 1))?;
43
+ class.define_method("message", method!(ExecutionError::message, 0))?;
44
+ class.define_method("==", method!(ExecutionError::eql, 1))?;
45
+ class.define_method("inspect", method!(ExecutionError::inspect, 0))?;
46
+
47
+ Ok(())
48
+ }
@@ -0,0 +1,40 @@
1
+ use super::root;
2
+ use super::ExecutionError;
3
+ use crate::helpers::{WrappedStruct};
4
+ use magnus::{gc, Error, Module, RArray, Value, TypedData, DataTypeFunctions, method};
5
+
6
+ #[derive(Clone, Debug, TypedData)]
7
+ #[magnus(class = "Bluejay::ExecutionResult", mark)]
8
+ pub struct ExecutionResult {
9
+ value: Value,
10
+ errors: Vec<WrappedStruct<ExecutionError>>,
11
+ }
12
+
13
+ impl ExecutionResult {
14
+ pub fn new(value: Value, errors: Vec<WrappedStruct<ExecutionError>>) -> Self {
15
+ Self { value, errors }
16
+ }
17
+
18
+ fn value(&self) -> Value {
19
+ self.value
20
+ }
21
+
22
+ fn errors(&self) -> RArray {
23
+ RArray::from_iter(self.errors.iter().map(|err| *err))
24
+ }
25
+ }
26
+
27
+ impl DataTypeFunctions for ExecutionResult {
28
+ fn mark(&self) {
29
+ gc::mark(&self.value);
30
+ self.errors.iter().for_each(WrappedStruct::mark);
31
+ }
32
+ }
33
+
34
+ pub fn init() -> Result<(), Error> {
35
+ let class = root().define_class("ExecutionResult", Default::default())?;
36
+ class.define_method("value", method!(ExecutionResult::value, 0))?;
37
+ class.define_method("errors", method!(ExecutionResult::errors, 0))?;
38
+
39
+ Ok(())
40
+ }