open_rpc/
lib.rs

1//! Defines the types defined by the [OpenRPC] specification.
2//!
3//! [OpenRPC](spec.open-rpc.org)
4
5#![cfg_attr(not(feature = "std"), no_std)]
6#![warn(missing_docs, missing_debug_implementations)]
7
8extern crate alloc;
9
10use alloc::collections::BTreeMap;
11use alloc::vec::Vec;
12
13use serde::{Deserialize, Serialize};
14
15/// Represents an OpenRPC document.
16#[derive(Serialize, Deserialize, Debug, Clone)]
17#[serde(rename_all = "camelCase")]
18pub struct OpenRpc {
19    /// The semantic version number of the OpenRPC Specification version that the OpenRPC document
20    /// uses.
21    ///
22    /// This field should be used by tooling specifications and clients to interpret the OpenRPC
23    /// document.
24    #[cfg_attr(feature = "relaxed", serde(default = "serde_fns::openrpc_version"))]
25    pub openrpc: String,
26    /// Provides metadata about the API.
27    ///
28    /// This metadata may be used by tooling as required.
29    #[cfg_attr(feature = "relaxed", serde(default = "serde_fns::info"))]
30    pub info: Info,
31    /// An array of [`Server`] objects, which provide connectivity information to a target server.
32    ///
33    /// If the `servers` property is not provided, or is an empty array, the default value would
34    /// be a [`Server`] with a `url` value of `localhost`. This is taken care of by the
35    /// [`open-rpc`](crate) crate.
36    #[serde(default = "serde_fns::servers")]
37    pub servers: Vec<Server>,
38    /// The available methods for the API. While this field is required, it is legal to leave it
39    /// empty.
40    pub methods: Vec<RefOr<Method>>,
41    /// Holds various schemas for the specification.
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub components: Option<Components>,
44    /// Contains additional documentation.
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub external_docs: Option<ExternalDocumentation>,
47}
48
49impl OpenRpc {
50    /// Returns the [`Method`] with the given path reference.
51    ///
52    /// # Examples
53    ///
54    /// ```no_run
55    /// let path = "#/components/schemas/MY_SCHEMA";
56    /// let schema = openrpc.get_schema(path).unwrap();
57    /// ```
58    pub fn get_schema(&self, reference: &str) -> Option<&Schema> {
59        let mut components = reference.split('/');
60
61        if !matches!(components.next(), Some("#")) {
62            return None;
63        }
64
65        if !matches!(components.next(), Some("components")) {
66            return None;
67        }
68
69        if !matches!(components.next(), Some("schemas")) {
70            return None;
71        }
72
73        let name = components.next()?;
74        self.components.as_ref()?.schemas.get(name)
75    }
76
77    /// Returns the [`Error`] with the given path reference.
78    ///
79    /// # Examples
80    ///
81    /// ```no_run
82    /// let path = "#/components/errors/MY_ERROR";
83    /// let schema = openrpc.get_schema(path).unwrap();
84    /// ```
85    pub fn get_error(&self, reference: &str) -> Option<&Error> {
86        let mut components = reference.split('/');
87
88        if !matches!(components.next(), Some("#")) {
89            return None;
90        }
91
92        if !matches!(components.next(), Some("components")) {
93            return None;
94        }
95
96        if !matches!(components.next(), Some("errors")) {
97            return None;
98        }
99
100        let name = components.next()?;
101        self.components.as_ref()?.errors.get(name)
102    }
103
104    /// Returns the [`ContentDescriptor`] with the given path reference.
105    ///
106    /// # Examples
107    ///
108    /// ```no_run
109    /// let path = "#/components/errors/MY_ERROR";
110    /// let schema = openrpc.get_schema(path).unwrap();
111    /// ```
112    pub fn get_content_descriptor(&self, reference: &str) -> Option<&ContentDescriptor> {
113        let mut components = reference.split('/');
114
115        if !matches!(components.next(), Some("#")) {
116            return None;
117        }
118
119        if !matches!(components.next(), Some("components")) {
120            return None;
121        }
122
123        if !matches!(components.next(), Some("contentDescriptors")) {
124            return None;
125        }
126
127        let name = components.next()?;
128        self.components.as_ref()?.content_descriptors.get(name)
129    }
130}
131
132/// Provides metadata about the API.
133///
134/// The metadata may be used by clients if needed, and may be presented in editing or
135/// documentation generation tools for convenience.
136#[derive(Serialize, Deserialize, Debug, Clone)]
137#[serde(rename_all = "camelCase")]
138pub struct Info {
139    /// The title of the application.
140    #[cfg_attr(feature = "relaxed", serde(default))]
141    pub title: String,
142    /// A verbose description of the application.
143    ///
144    /// GitHub Flavored Markdown syntax may be used for rich text representation.
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub description: Option<String>,
147    /// A URL to the Terms of Service for the API.
148    ///
149    /// This must contain an URL.
150    #[serde(default, skip_serializing_if = "Option::is_none")]
151    pub terms_of_service: Option<String>,
152    /// contact information for the exposed API.
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    pub contact: Option<Contact>,
155    /// License information for the exposed API.
156    #[serde(default, skip_serializing_if = "Option::is_none")]
157    pub license: Option<License>,
158    /// The version of the OpenRPC document.
159    ///
160    /// Note that this is distinct from the `openrpc` field of [`OpenRpc`] which specifies the
161    /// version of the OpenRPC Specification used.
162    #[cfg_attr(feature = "relaxed", serde(default))]
163    pub version: String,
164}
165
166/// Contact information for the exposed API.
167#[derive(Serialize, Deserialize, Debug, Clone)]
168#[serde(rename_all = "camelCase")]
169pub struct Contact {
170    /// The identifying name of the contact person/organization.
171    #[cfg_attr(
172        feature = "serde",
173        serde(default, skip_serializing_if = "Option::is_none")
174    )]
175    pub name: Option<String>,
176    /// The URL pointing to the contact information.
177    ///
178    /// This must contain an URL.
179    #[cfg_attr(
180        feature = "serde",
181        serde(default, skip_serializing_if = "Option::is_none")
182    )]
183    pub url: Option<String>,
184    /// The email address of the contact person/organization.
185    ///
186    /// This must contain an email address.
187    #[cfg_attr(
188        feature = "serde",
189        serde(default, skip_serializing_if = "Option::is_none")
190    )]
191    pub email: Option<String>,
192}
193
194/// License information for the exposed API.
195#[derive(Serialize, Deserialize, Debug, Clone)]
196#[serde(rename_all = "camelCase")]
197pub struct License {
198    /// The name of the license used for the API.
199    #[cfg_attr(feature = "relaxed", serde(default))]
200    pub name: String,
201    /// The URL pointing to the license used for the API.
202    ///
203    /// This must contain an URL.
204    #[serde(default, skip_serializing_if = "Option::is_none")]
205    pub url: Option<String>,
206}
207
208/// A server.
209#[derive(Serialize, Deserialize, Debug, Clone)]
210#[serde(rename_all = "camelCase")]
211pub struct Server {
212    /// A name to be used as the cannonical name for the server.
213    #[cfg_attr(feature = "relaxed", serde(default))]
214    pub name: String,
215    /// A URL to the target host.
216    ///
217    /// This URL supports Server Variables and may be relative to indicate that the host location
218    /// is relative to the location where the OpenRPC document is being served.
219    ///
220    /// Server Variables are passed into the Runtime Expression to produce a server URL.
221    #[cfg_attr(feature = "relaxed", serde(default = "serde_fns::runtime_expr"))]
222    pub url: RuntimeExpression,
223    /// A short description of what the server is.
224    #[serde(default, skip_serializing_if = "Option::is_none")]
225    pub summary: Option<String>,
226    /// Describes the host designated by the URL.
227    ///
228    /// GitHub Flavored Markdown may be used for rich text presentation.
229    #[serde(default, skip_serializing_if = "Option::is_none")]
230    pub description: Option<String>,
231    /// The values of this object are passed to the [`RuntimeExpression`] to produce an actual
232    /// URL.
233    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
234    pub variables: BTreeMap<String, ServerVariable>,
235}
236
237/// An object representing a Server Variable for server URL template substitution.
238#[derive(Serialize, Deserialize, Debug, Clone, Default)]
239#[serde(rename_all = "camelCase")]
240pub struct ServerVariable {
241    /// An enumeration of string values to be used if the substitution options are from a limited
242    /// set.
243    #[serde(default, skip_serializing_if = "Vec::is_empty")]
244    pub enum_: Vec<String>,
245    /// The default value to use for substitution, which shall be sent if an alternate value is
246    /// not supplied.
247    ///
248    /// Note this behavior is different than the Schema Object's treatement of default values,
249    /// because in those cases parameter values are optional.
250    #[cfg_attr(feature = "relaxed", serde(default))]
251    pub default: String,
252    /// An optional description for the server variable.
253    ///
254    /// GitHub Flavored Markdown syntax may be used for rich text representation.
255    #[serde(default, skip_serializing_if = "Option::is_none")]
256    pub description: Option<String>,
257}
258
259/// Describes the interface for the given method name.
260///
261/// The method name is used as the `method` field of the JSON-RPC body. It therefore must be
262/// unique.
263#[derive(Serialize, Deserialize, Debug, Clone)]
264#[serde(rename_all = "camelCase")]
265pub struct Method {
266    /// The cannonical name of the method.
267    ///
268    /// This name must be unique within the methods array.
269    #[cfg_attr(feature = "relaxed", serde(default))]
270    pub name: String,
271    /// A list of tags for API documentation control. Tags can be used for logical grouping
272    /// of methods by resources or any other qualifier.
273    #[serde(default, skip_serializing_if = "Vec::is_empty")]
274    pub tags: Vec<RefOr<Tag>>,
275    /// A short summary of what the method does.
276    #[serde(default, skip_serializing_if = "Option::is_none")]
277    pub summary: Option<String>,
278    /// A verbose explanation of the method behavior.
279    ///
280    /// GitHub Flavored Markdown syntax may be used for rich text representation.
281    #[serde(default, skip_serializing_if = "Option::is_none")]
282    pub description: Option<String>,
283    /// Additional external documentation for this method.
284    #[serde(default, skip_serializing_if = "Option::is_none")]
285    pub external_docs: Option<ExternalDocumentation>,
286    /// A list of parameters that are applicable for this method.
287    ///
288    /// The list must not include duplicated parameters and therefore require `name` to be
289    /// unique.
290    ///
291    /// All required parameters must be listed *before* any optional parameters.
292    #[cfg_attr(feature = "relaxed", serde(default))]
293    pub params: Vec<RefOr<ContentDescriptor>>,
294    /// The description of the result returned by the method.
295    ///
296    /// If defined, it must be a [`ContentDescriptor`] or a [`Reference`].
297    ///
298    /// If undefined, the method must only be used as a *notification*.
299    #[serde(default, skip_serializing_if = "Option::is_none")]
300    pub result: Option<RefOr<ContentDescriptor>>,
301    /// Declares this method as deprecated.
302    ///
303    /// Consumers should refrain from usage of the declared method.
304    ///
305    /// The default value is `false`.
306    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
307    pub deprecated: bool,
308    /// An alternative `servers` array to service this method.
309    ///
310    /// If specified, it overrides the `servers` array defined at the root level.
311    #[serde(default, skip_serializing_if = "Option::is_none")]
312    pub servers: Option<Vec<Server>>,
313    /// A list of custom application-defined errors that may be returned.
314    ///
315    /// The errors must have unique error codes.
316    #[serde(default, skip_serializing_if = "Vec::is_empty")]
317    pub errors: Vec<RefOr<Error>>,
318    /// A list of possible links from this method call.
319    #[serde(default, skip_serializing_if = "Vec::is_empty")]
320    pub links: Vec<RefOr<Link>>,
321    /// The expected format of the parameters.
322    ///
323    /// The parameters of a method may be an array, an object, or either. When a method
324    /// has a `param_structure` value of [`ByName`], callers of the method must pass an
325    /// object as the parameters. When a method has a `param_structure` value of [`ByPosition`],
326    /// callers of the method must pass an array as the parameters. Otherwise, callers may
327    /// pass either an array or an object as the parameters.
328    ///
329    /// The default value is [`Either`].
330    ///
331    /// [`ByName`]: ParamStructure::ByName
332    /// [`ByPosition`]: ParamStructure::ByPosition
333    /// [`Either`]: ParamStructure::Either
334    #[serde(default, skip_serializing_if = "serde_fns::is_default")]
335    pub param_structure: ParamStructure,
336    /// An array of [`ExamplePairing`] objects, where each example includes a valid
337    /// params-to-result [`ContentDescriptor`] pairing.
338    #[serde(default, skip_serializing_if = "Vec::is_empty")]
339    pub examples: Vec<RefOr<ExamplePairing>>,
340}
341
342/// A possible value for the `param_structure` field of [`Method`].
343#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
344#[serde(rename_all = "kebab-case")]
345pub enum ParamStructure {
346    /// Parameters must be passed as a JSON object.
347    ByName,
348    /// Parameters must be passed as a JSON array.
349    ByPosition,
350    /// Parameters may be passed as either a JSON object or a JSON array.
351    #[default]
352    Either,
353}
354
355/// Content descriptors are that do just as they suggest - describe content. They are reusable
356/// ways of describing either parameters or results.
357#[derive(Serialize, Deserialize, Debug, Clone)]
358#[serde(rename_all = "camelCase")]
359pub struct ContentDescriptor {
360    /// The name of the content being described.
361    ///
362    /// If the content described is a method parameter assignable [`ByName`](ParamStructure::ByName),
363    /// this field must be the name of the parameter.
364    #[cfg_attr(feature = "relaxed", serde(default))]
365    pub name: String,
366    /// A short summary of the content that is being described.
367    #[serde(default, skip_serializing_if = "Option::is_none")]
368    pub summary: Option<String>,
369    /// A verbose explanation of the content being described.
370    ///
371    /// GitHub Flavored Markdown syntax may be used for rich text representation.
372    #[serde(default, skip_serializing_if = "Option::is_none")]
373    pub description: Option<String>,
374    /// Determines if the content is a required field.
375    ///
376    /// Default is `false`.
377    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
378    pub required: bool,
379    /// A [`Schema`] that describes what is allowed in the content.
380    #[cfg_attr(feature = "relaxed", serde(default))]
381    pub schema: Schema,
382    /// Whether the content is deprecated.
383    ///
384    /// Default is `false`.
385    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
386    pub deprecated: bool,
387}
388
389/// Allows the definition of input and output data types.
390#[derive(Serialize, Deserialize, Debug, Clone, Default)]
391#[serde(rename_all = "camelCase")]
392pub struct Schema {
393    /// The title of the schema.
394    #[serde(default, skip_serializing_if = "Option::is_none")]
395    pub title: Option<String>,
396    /// The description of the schema.
397    #[serde(default, skip_serializing_if = "Option::is_none")]
398    pub description: Option<String>,
399    /// The contents of the schema.
400    #[serde(flatten)]
401    pub contents: SchemaContents,
402}
403
404/// The content of a schema.
405#[derive(Serialize, Deserialize, Debug, Clone)]
406#[serde(untagged)]
407pub enum SchemaContents {
408    /// The schema contains a reference to another schema.
409    Reference {
410        /// The reference string.
411        #[serde(rename = "$ref")]
412        reference: String,
413    },
414    /// The schema contains a literal value.
415    Literal(Literal),
416    /// The schema is made of a combination of other schemas.
417    ///
418    /// The final object must match *all* of the schemas.
419    AllOf {
420        /// The schemas that the final object must match.
421        #[serde(rename = "allOf")]
422        all_of: Vec<Schema>,
423    },
424    /// The schema is made of a combination of other schemas.
425    ///
426    /// The final object must match *any* of the schemas.
427    AnyOf {
428        /// The schemas that the final object must match.
429        #[serde(rename = "anyOf")]
430        any_of: Vec<Schema>,
431    },
432    /// The schema is made of a combination of other schemas.
433    ///
434    /// The final object must match exactly *one* of the schemas.
435    OneOf {
436        /// The schemas that the final object must match.
437        #[serde(rename = "oneOf")]
438        one_of: Vec<Schema>,
439    },
440}
441
442impl Default for SchemaContents {
443    #[inline]
444    fn default() -> Self {
445        Self::Literal(Literal::Null)
446    }
447}
448
449/// A literal value.
450#[derive(Serialize, Deserialize, Debug, Clone)]
451#[serde(tag = "type", rename_all = "lowercase")]
452pub enum Literal {
453    /// The literal is a boolean.
454    Boolean,
455    /// The literal is an integer.
456    Integer(IntegerLiteral),
457    /// The literal is a number.
458    Number(NumberLiteral),
459    /// The literal is an array.
460    Array(ArrayLiteral),
461    /// The literal is a string.
462    String(StringLiteral),
463    /// The literal is an object.
464    Object(ObjectLiteral),
465    /// The literal is a null value.
466    Null,
467}
468
469/// The constraints that may be applied to an integer literal schema.
470#[derive(Serialize, Deserialize)]
471#[serde(rename_all = "camelCase")]
472#[derive(Debug, Clone)]
473pub struct IntegerLiteral {
474    /// The integer must be a multiple of this value.
475    #[serde(default, skip_serializing_if = "Option::is_none")]
476    pub multiple_of: Option<i64>,
477    /// The minimum value of the integer.
478    #[serde(default, skip_serializing_if = "Option::is_none")]
479    pub minimum: Option<i64>,
480    /// The maximum value of the integer.
481    #[serde(default, skip_serializing_if = "Option::is_none")]
482    pub maximum: Option<i64>,
483    /// Whether the minimum value is exclusive.
484    ///
485    /// Default is `false`.
486    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
487    pub exclusive_minimum: bool,
488    /// Whether the maximum value is exclusive.
489    ///
490    /// Default is `false`.
491    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
492    pub exclusive_maximum: bool,
493}
494
495/// The constraints that may be applied to a number literal schema.
496#[derive(Serialize, Deserialize, Debug, Clone)]
497#[serde(rename_all = "camelCase")]
498pub struct NumberLiteral {
499    /// The number must be a multiple of this value.
500    #[serde(default, skip_serializing_if = "Option::is_none")]
501    pub multiple_of: Option<f64>,
502    /// The minimum value of the number.
503    #[serde(default, skip_serializing_if = "Option::is_none")]
504    pub minimum: Option<f64>,
505    /// The maximum value of the number.
506    #[serde(default, skip_serializing_if = "Option::is_none")]
507    pub maximum: Option<f64>,
508    /// Whether the minimum value is exclusive.
509    ///
510    /// Default is `false`.
511    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
512    pub exclusive_minimum: bool,
513    /// Whether the maximum value is exclusive.
514    ///
515    /// Default is `false`.
516    #[serde(default, skip_serializing_if = "serde_fns::is_false")]
517    pub exclusive_maximum: bool,
518}
519
520/// The constraints that may be applied to an array literal schema.
521#[derive(Serialize, Deserialize, Debug, Clone)]
522#[serde(rename_all = "camelCase")]
523pub struct ArrayLiteral {
524    /// The schema that the items in the array must match.
525    #[serde(default, skip_serializing_if = "Option::is_none")]
526    pub items: Option<Box<Schema>>,
527}
528
529/// The constraints that may be applied to an string literal schema.
530#[derive(Serialize, Deserialize, Debug, Clone)]
531#[serde(rename_all = "camelCase")]
532pub struct StringLiteral {
533    /// The minimum length of the string.
534    #[serde(default, skip_serializing_if = "Option::is_none")]
535    pub min_length: Option<u64>,
536    /// The maximum length of the string.s
537    #[serde(default, skip_serializing_if = "Option::is_none")]
538    pub max_length: Option<u64>,
539    /// The pattern that the string must match.
540    #[serde(default, skip_serializing_if = "Option::is_none")]
541    pub pattern: Option<String>,
542    /// The format that the string must be in.
543    #[serde(default, skip_serializing_if = "Option::is_none")]
544    pub format: Option<StringFormat>,
545    /// A list of possible values for the string.
546    #[serde(default, skip_serializing_if = "Option::is_none", rename = "enum")]
547    pub enumeration: Option<Vec<String>>,
548}
549
550/// A string format.
551#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
552#[serde(rename_all = "kebab-case")]
553pub enum StringFormat {
554    /// Date and time together, for example, `2018-11-13T20:20:39+00:00`.
555    DateTime,
556    /// Time, for example, `20:20:39+00:00`.
557    Time,
558    /// Date, for example, `2018-11-13`.
559    Date,
560    /// A duration as defined by the [ISO 8601 ABNF](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A).
561    Duration,
562    /// An email. See [RFC 5321](http://tools.ietf.org/html/rfc5321#section-4.1.2).
563    Email,
564    /// The internationalized version of an email. See [RFC 6531](https://tools.ietf.org/html/rfc6531).
565    IdnEmail,
566    /// A host name. See [RFC 1123](https://datatracker.ietf.org/doc/html/rfc1123#section-2.1).
567    Hostname,
568    /// The internationalized version of a host name. See [RFC 5890](https://tools.ietf.org/html/rfc5890#section-2.3.2.3).
569    IdnHostname,
570    /// An IP v4. See [RFC 2673](http://tools.ietf.org/html/rfc2673#section-3.2).
571    #[serde(rename = "ipv4")]
572    IpV4,
573    /// An IP v6. See [RFC 2373](http://tools.ietf.org/html/rfc2373#section-2.2).
574    #[serde(rename = "ipv6")]
575    IpV6,
576    /// A universally unique identifier. See [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122).
577    Uuid,
578    /// A universal resource identifier . See [RFC 3986](http://tools.ietf.org/html/rfc3986).
579    Uri,
580    /// A URI reference. See (RFC 3986)[http://tools.ietf.org/html/rfc3986#section-4.1].
581    UriReference,
582    /// The internationalized version of a URI. See [RFC 3987](https://tools.ietf.org/html/rfc3987).
583    Iri,
584    /// The internationalized version of a URI reference. See [RFC 3987](https://tools.ietf.org/html/rfc3987).
585    IriReference,
586    /// A URI template. See [RFC 6570](https://tools.ietf.org/html/rfc6570).
587    UriTemplate,
588    /// A JSON pointer. See [RFC 6901](https://tools.ietf.org/html/rfc6901).
589    JsonPointer,
590    /// A relative JSON pointer. See [Relative JSON Pointer](https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01).
591    RelativeJsonPointer,
592    /// A regular expression. See [ECMA 262](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/).
593    Regex,
594}
595
596/// The constraints that may be applied to an object literal schema.
597#[derive(Serialize, Deserialize, Debug, Clone)]
598#[serde(rename_all = "camelCase")]
599pub struct ObjectLiteral {
600    /// The properties that the object might have.
601    pub properties: BTreeMap<String, Schema>,
602    /// A list of properties that the object must have.
603    #[serde(default, skip_serializing_if = "Vec::is_empty")]
604    pub required: Vec<String>,
605}
606
607/// A set of example parameters and a result.
608///
609/// This result is what you'd expect from the JSON-RPC service given the exact params.
610#[derive(Serialize, Deserialize, Debug, Clone)]
611#[serde(rename_all = "camelCase")]
612pub struct ExamplePairing {
613    /// The name for the example pairing.
614    #[cfg_attr(feature = "relaxed", serde(default))]
615    pub name: String,
616    /// A verbose description of the example pairing.
617    #[serde(default, skip_serializing_if = "Option::is_none")]
618    pub description: Option<String>,
619    /// A short summary of the example pairing.
620    #[serde(default, skip_serializing_if = "Option::is_none")]
621    pub summary: Option<String>,
622    /// Example parameters.
623    #[cfg_attr(feature = "relaxed", serde(default))]
624    pub params: Vec<RefOr<ExampleObject>>,
625    /// Example result.
626    ///
627    /// When undefined, shows the usage of the method as a notification.
628    #[cfg_attr(feature = "relaxed", serde(default))]
629    pub result: RefOr<ExampleObject>,
630}
631
632/// Defines an example that is intended to match a [`Schema`] of a given [`ContentDescriptor`].
633#[derive(Serialize, Deserialize, Default, Debug, Clone)]
634#[serde(rename_all = "camelCase")]
635pub struct ExampleObject {
636    /// Cannonical name of the example.
637    #[serde(default, skip_serializing_if = "Option::is_none")]
638    pub name: Option<String>,
639    /// A verbose description of the example
640    ///
641    /// GitHub Flavored Markdown syntax may be used for rich text representation.
642    #[serde(default, skip_serializing_if = "Option::is_none")]
643    pub description: Option<String>,
644    /// A short summary of the example.
645    #[serde(default, skip_serializing_if = "Option::is_none")]
646    pub summary: Option<String>,
647    /// The value of the example.
648    #[serde(default, skip_serializing_if = "Option::is_none")]
649    pub value: Option<ExampleValue>,
650}
651
652/// The example value of an [`ExampleObject`].
653#[derive(Serialize, Deserialize, Debug, Clone)]
654pub enum ExampleValue {
655    /// The value is a JSON object embedded in the document.
656    #[cfg(feature = "serde_json")]
657    #[serde(rename = "value")]
658    Value(serde_json::Value),
659    /// The value is a JSON object embedded in the document.
660    /// A link to an external document containing the value.
661    #[serde(rename = "externalValue")]
662    External(String),
663}
664
665/// Represents a possible design-time link for a result.
666///
667/// The presence of a link does not guarantee the caller's ability to successfully invoke it,
668/// rather it provides a known relationship and traversal mechanism between results and other
669/// methods.
670#[derive(Serialize, Deserialize)]
671#[serde(rename_all = "camelCase")]
672#[derive(Debug, Clone)]
673pub struct Link {
674    /// Cannonical name for the link.
675    #[cfg_attr(feature = "relaxed", serde(default))]
676    pub name: String,
677    /// A description of the link.
678    ///
679    /// GitHub Flavored Markdown syntax may be used for rich text representation.
680    #[serde(default, skip_serializing_if = "Option::is_none")]
681    pub description: Option<String>,
682    /// Short description for the link.
683    #[serde(default, skip_serializing_if = "Option::is_none")]
684    pub summary: Option<String>,
685    /// The name of an *existing*, resolvable OpenRPC method, as defined with a unique
686    /// `method`. This field must resolve to a unique [`Method`] object.
687    #[serde(default, skip_serializing_if = "Option::is_none")]
688    pub method: Option<String>,
689    /// The parameters to pass to a method as specified with `method`. The key is the parameter
690    /// name to be used, whereas the value can be a constant or a [`RuntimeExpression`] to be
691    /// evaluated and passed to the linked method.
692    #[serde(default, skip_serializing_if = "Option::is_none")]
693    pub params: Option<LinkParams>,
694    /// A server object to be used by the target method.
695    #[serde(default, skip_serializing_if = "Option::is_none")]
696    pub server: Option<Server>,
697}
698
699/// The content of the `params` field of a [`Link`].
700#[derive(Serialize, Deserialize, Debug, Clone)]
701#[serde(untagged)]
702pub enum LinkParams {
703    /// A [`RuntimeExpression`] that evaluates to the parameters.
704    Dynamic(RuntimeExpression),
705    /// Some constant parameters.
706    #[cfg(feature = "serde_json")]
707    Constant(BTreeMap<String, serde_json::Value>),
708}
709
710/// Runtime expressions allow the user to define an expression which will evaluate to a
711/// string once the desired value(s) are known.
712///
713/// They are used when the desired value of a link or server can only be constructed at
714/// run time. This mechanism is used by [`Link`] objects and [`ServerVariable`]s.
715///
716/// This runtime expression makes use of JSON template strings.
717#[derive(Serialize, Deserialize, Debug, Clone)]
718#[serde(transparent)]
719pub struct RuntimeExpression(pub String);
720
721/// An application-level error.
722#[derive(Serialize, Deserialize, Debug, Clone)]
723#[serde(rename_all = "camelCase")]
724pub struct Error {
725    /// An application-defined error code.
726    #[cfg_attr(feature = "relaxed", serde(default))]
727    pub code: i64,
728    /// A string providing a short description of the error.
729    ///
730    /// The message should be limited to a concise single sentence.
731    #[cfg_attr(feature = "relaxed", serde(default))]
732    pub message: String,
733    /// A value that contains additional information about the error.
734    ///
735    /// This may be omitted. The specifics of this value are defined by the server.
736    #[serde(default, skip_serializing_if = "Option::is_none")]
737    #[cfg(feature = "serde_json")]
738    pub data: Option<serde_json::Value>,
739}
740
741/// Holds a set of reusable objects for different aspects of the OpenRPC document.
742///
743/// All objects defined within the [`Components`] object will have no effect on the API
744/// unless they are explicitely referenced from properties outside of the [`Components`]
745/// object.
746#[derive(Serialize, Deserialize, Debug, Clone)]
747#[serde(rename_all = "camelCase")]
748pub struct Components {
749    /// A list of reusable [`ContentDescriptor`]s.
750    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
751    pub content_descriptors: BTreeMap<String, ContentDescriptor>,
752    /// A list of reusable [`Schema`]s.
753    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
754    pub schemas: BTreeMap<String, Schema>,
755    /// A list of reusable [`ExampleObject`]s.
756    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
757    pub examples: BTreeMap<String, ExampleObject>,
758    /// A list of reusable [`Link`]s.
759    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
760    pub links: BTreeMap<String, Link>,
761    /// A list of reusable [`Error`]s.
762    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
763    pub errors: BTreeMap<String, Error>,
764    /// A list of reusable [`ExamplePairing`]s.
765    #[serde(
766        default,
767        skip_serializing_if = "BTreeMap::is_empty",
768        rename = "examplePairingObjects"
769    )]
770    pub example_pairings: BTreeMap<String, ExamplePairing>,
771    /// A list of reusable [`Tag`]s.
772    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
773    pub tags: BTreeMap<String, Tag>,
774}
775
776/// Adds metadata to a single tag that is used by the [`Method`] Object.
777///
778/// It is not mandatory to have a [`Tag`] Object per tag defined in the [`Method`]
779/// Object instances.
780#[derive(Serialize, Deserialize, Debug, Clone)]
781#[serde(rename_all = "camelCase")]
782pub struct Tag {
783    /// The name of the tag.
784    #[cfg_attr(feature = "relaxed", serde(default))]
785    pub name: String,
786    /// A short summary of the tag.
787    #[serde(default, skip_serializing_if = "Option::is_none")]
788    pub summary: Option<String>,
789    /// A verbose explanation of the tag.
790    ///
791    /// GitHub Flavored Markdown syntax may be used for rich text representation.
792    #[serde(default, skip_serializing_if = "Option::is_none")]
793    pub description: Option<String>,
794    /// Additional external documentation for this tag.
795    #[serde(default, skip_serializing_if = "Option::is_none")]
796    pub external_docs: Option<ExternalDocumentation>,
797}
798
799/// Allows referencing an external resource for extended documentation.
800#[derive(Serialize, Deserialize, Debug, Clone)]
801#[serde(rename_all = "camelCase")]
802pub struct ExternalDocumentation {
803    /// A verbose explanation of the target documentation.
804    ///
805    /// GitHub Flavored Markdown syntax may be used for rich text representation.
806    #[serde(default, skip_serializing_if = "Option::is_none")]
807    pub description: Option<String>,
808    /// A URL for the target documentation.
809    ///
810    /// This must contain an URL.
811    #[cfg_attr(feature = "relaxed", serde(default))]
812    pub url: String,
813}
814
815/// Either a reference or an inline object.
816#[derive(Serialize, Deserialize, Debug, Clone)]
817#[serde(untagged)]
818pub enum RefOr<T> {
819    /// A reference to an object defined elsewhere.
820    Reference {
821        /// The reference string.
822        #[serde(rename = "$ref")]
823        reference: String,
824    },
825    /// An inline object.
826    Inline(T),
827}
828
829impl<T: Default> Default for RefOr<T> {
830    #[inline]
831    fn default() -> Self {
832        RefOr::Inline(T::default())
833    }
834}
835
836/// Functions used by `serde`, such as predicates and default values.
837mod serde_fns {
838    use alloc::collections::BTreeMap;
839
840    use crate::{Info, RuntimeExpression, Server};
841
842    /// Returns the default value of the `servers` field of [`OpenRpc`].
843    pub fn servers() -> Vec<Server> {
844        alloc::vec![Server {
845            name: "default".into(),
846            url: RuntimeExpression("localhost".into()),
847            summary: None,
848            description: None,
849            variables: BTreeMap::new(),
850        }]
851    }
852
853    /// Returns whether `b` is `false`.
854    #[inline]
855    pub fn is_false(b: &bool) -> bool {
856        !*b
857    }
858
859    /// Returns whether the given value is the default value of its type.
860    pub fn is_default<T: Default + PartialEq>(t: &T) -> bool {
861        *t == T::default()
862    }
863
864    /// Returns the default vlaue
865    #[cfg(feature = "relaxed")]
866    pub fn runtime_expr() -> RuntimeExpression {
867        RuntimeExpression(String::new())
868    }
869
870    /// Returns the default openRPC version.
871    #[cfg(feature = "relaxed")]
872    pub fn openrpc_version() -> String {
873        "1.0.0-rc1".into()
874    }
875
876    /// Returns a default instance of [`Info`].
877    #[cfg(feature = "relaxed")]
878    pub fn info() -> Info {
879        Info {
880            title: "OpenRPC API".into(),
881            description: None,
882            terms_of_service: None,
883            contact: None,
884            license: None,
885            version: "1.0.0-rc1".into(),
886        }
887    }
888}