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}