linera_base/
graphql.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4/// Defines a GraphQL scalar with a description string.
5///
6/// This is equivalent to `scalar!` but always uses the stringified identifier as the name.
7#[macro_export]
8macro_rules! doc_scalar {
9    ($ty:ty, $desc:literal) => {
10        $crate::async_graphql::scalar_internal!(
11            $ty,
12            ::std::stringify!($ty),
13            ::std::option::Option::Some(::std::string::ToString::to_string($desc)),
14            ::std::option::Option::None
15        );
16    };
17}
18
19/// An error trying to parse the hex-digits of a BCS-encoded value.
20#[derive(thiserror::Error, Debug)]
21#[allow(missing_docs)]
22pub enum BcsHexParseError {
23    #[error("Invalid BCS: {0}")]
24    Bcs(#[from] bcs::Error),
25    #[error("Invalid hexadecimal: {0}")]
26    Hex(#[from] hex::FromHexError),
27}
28
29/// Defines a GraphQL scalar type using the hex-representation of the value's BCS-serialized form.
30///
31/// This is a modified implementation of [`async_graphql::scalar`].
32/// In addition, it implements `Display` and `FromStr`, also using hex-representation.
33#[macro_export]
34macro_rules! bcs_scalar {
35    ($ty:ty, $desc:literal) => {
36        impl $crate::async_graphql::ScalarType for $ty {
37            fn parse(
38                value: $crate::async_graphql::Value,
39            ) -> $crate::async_graphql::InputValueResult<Self> {
40                let hex: String = $crate::async_graphql::from_value(value)?;
41                let bytes = $crate::hex::decode(&hex)?;
42                let result = $crate::bcs::from_bytes(&bytes)?;
43                ::std::result::Result::Ok(result)
44            }
45
46            fn to_value(&self) -> $crate::async_graphql::Value {
47                let ::std::result::Result::Ok(bytes) = $crate::bcs::to_bytes(self) else {
48                    return $crate::async_graphql::Value::Null;
49                };
50                let hex = $crate::hex::encode(&bytes);
51                $crate::async_graphql::to_value(hex)
52                    .unwrap_or_else(|_| $crate::async_graphql::Value::Null)
53            }
54        }
55
56        impl $crate::async_graphql::InputType for $ty {
57            type RawValueType = Self;
58
59            fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
60                ::std::borrow::Cow::Borrowed(::std::stringify!($ty))
61            }
62
63            fn create_type_info(
64                registry: &mut $crate::async_graphql::registry::Registry,
65            ) -> ::std::string::String {
66                registry.create_input_type::<$ty, _>(
67                    $crate::async_graphql::registry::MetaTypeId::Scalar,
68                    |_| $crate::async_graphql::registry::MetaType::Scalar {
69                        name: ::std::borrow::ToOwned::to_owned(::std::stringify!($ty)),
70                        description: ::std::option::Option::Some(
71                            ::std::string::ToString::to_string($desc),
72                        ),
73                        is_valid: ::std::option::Option::Some(::std::sync::Arc::new(|value| {
74                            <$ty as $crate::async_graphql::ScalarType>::is_valid(value)
75                        })),
76                        visible: ::std::option::Option::None,
77                        inaccessible: false,
78                        tags: ::std::default::Default::default(),
79                        specified_by_url: ::std::option::Option::None,
80                    },
81                )
82            }
83
84            fn parse(
85                value: ::std::option::Option<$crate::async_graphql::Value>,
86            ) -> $crate::async_graphql::InputValueResult<Self> {
87                <$ty as $crate::async_graphql::ScalarType>::parse(value.unwrap_or_default())
88            }
89
90            fn to_value(&self) -> $crate::async_graphql::Value {
91                <$ty as $crate::async_graphql::ScalarType>::to_value(self)
92            }
93
94            fn as_raw_value(&self) -> ::std::option::Option<&Self::RawValueType> {
95                ::std::option::Option::Some(self)
96            }
97        }
98
99        impl $crate::async_graphql::OutputType for $ty {
100            fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
101                ::std::borrow::Cow::Borrowed(::std::stringify!($ty))
102            }
103
104            fn create_type_info(
105                registry: &mut $crate::async_graphql::registry::Registry,
106            ) -> ::std::string::String {
107                registry.create_output_type::<$ty, _>(
108                    $crate::async_graphql::registry::MetaTypeId::Scalar,
109                    |_| $crate::async_graphql::registry::MetaType::Scalar {
110                        name: ::std::borrow::ToOwned::to_owned(::std::stringify!($ty)),
111                        description: ::std::option::Option::Some(
112                            ::std::string::ToString::to_string($desc),
113                        ),
114                        is_valid: ::std::option::Option::Some(::std::sync::Arc::new(|value| {
115                            <$ty as $crate::async_graphql::ScalarType>::is_valid(value)
116                        })),
117                        visible: ::std::option::Option::None,
118                        inaccessible: false,
119                        tags: ::std::default::Default::default(),
120                        specified_by_url: ::std::option::Option::None,
121                    },
122                )
123            }
124
125            async fn resolve(
126                &self,
127                _: &$crate::async_graphql::ContextSelectionSet<'_>,
128                _field: &$crate::async_graphql::Positioned<
129                    $crate::async_graphql::parser::types::Field,
130                >,
131            ) -> $crate::async_graphql::ServerResult<$crate::async_graphql::Value> {
132                ::std::result::Result::Ok($crate::async_graphql::ScalarType::to_value(self))
133            }
134        }
135
136        impl ::std::fmt::Display for $ty {
137            fn fmt(
138                &self,
139                f: &mut ::std::fmt::Formatter<'_>,
140            ) -> ::std::result::Result<(), ::std::fmt::Error> {
141                match $crate::bcs::to_bytes(self) {
142                    ::std::result::Result::Ok(bytes) => {
143                        ::std::fmt::Display::fmt(&$crate::hex::encode(&bytes), f)
144                    }
145                    ::std::result::Result::Err(_) => {
146                        ::std::write!(f, "invalid {}", ::std::stringify!($ty))
147                    }
148                }
149            }
150        }
151
152        impl ::std::str::FromStr for $ty {
153            type Err = $crate::BcsHexParseError;
154
155            fn from_str(s: &str) -> Result<Self, Self::Err> {
156                let bytes = $crate::hex::decode(s)?;
157                ::std::result::Result::Ok($crate::bcs::from_bytes(&bytes)?)
158            }
159        }
160    };
161}