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