Skip to main content

openrpc_types/
lib.rs

1//! A transcription of types from the [`OpenRPC` Specification](https://spec.open-rpc.org/).
2//!
3//! This library does NOT perform more complicated validation of the spec, including:
4//! - unique method names
5//! - unique error codes
6//! - reference idents
7//!
8//! `Link` objects are not currently supported.
9//!
10//! > When quoted, the specification will appear as blockquoted text, like so.
11
12use schemars::Schema;
13use semver::{BuildMetadata, Prerelease, Version};
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use std::collections::BTreeMap;
17use url::Url;
18
19pub use resolver::{resolve_within, BrokenReference};
20
21pub mod resolved;
22mod resolver;
23
24/// The version of the OpenRPC specification that this library was written against.
25pub const OPEN_RPC_SPECIFICATION_VERSION: Version = Version {
26    major: 1,
27    minor: 3,
28    patch: 2,
29    pre: Prerelease::EMPTY,
30    build: BuildMetadata::EMPTY,
31};
32
33/// > This is the root object of the OpenRPC document.
34/// > The contents of this object represent a whole OpenRPC document.
35/// > How this object is constructed or stored is outside the scope of the OpenRPC Specification.
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct OpenRPC {
39    /// > REQUIRED.
40    /// > This string MUST be the semantic version number of the OpenRPC Specification version that the OpenRPC document uses.
41    /// > The openrpc field SHOULD be used by tooling specifications and clients to interpret the OpenRPC document.
42    /// > This is not related to the API info.version string.
43    pub openrpc: Version,
44    /// > REQUIRED.
45    /// > Provides metadata about the API.
46    /// > The metadata MAY be used by tooling as required.
47    pub info: Info,
48    /// > An array of Server Objects,
49    /// > which provide connectivity information to a target server.
50    /// > If the servers property is not provided, or is an empty array,
51    /// > the default value would be a Server Object with a url value of `localhost`.
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub servers: Option<Vec<Server>>,
54    /// > REQUIRED.
55    /// > The available methods for the API.
56    /// > While it is required, the array may be empty (to handle security filtering, for example).
57    pub methods: Vec<ReferenceOr<Method>>,
58    /// > An element to hold various schemas for the specification.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub components: Option<Components>,
61    /// > Additional external documentation.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub external_docs: Option<ExternalDocumentation>,
64    #[serde(flatten)]
65    pub extensions: SpecificationExtensions,
66}
67
68impl Default for OpenRPC {
69    fn default() -> Self {
70        Self {
71            openrpc: OPEN_RPC_SPECIFICATION_VERSION,
72            info: Default::default(),
73            servers: Default::default(),
74            methods: Default::default(),
75            components: Default::default(),
76            external_docs: Default::default(),
77            extensions: Default::default(),
78        }
79    }
80}
81
82/// > The object provides metadata about the API.
83/// > The metadata MAY be used by the clients if needed,
84/// > and MAY be presented in editing or documentation generation tools for convenience.
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
86#[serde(rename_all = "camelCase")]
87pub struct Info {
88    /// > REQUIRED.
89    /// > The title of the application.
90    pub title: String,
91    /// > A verbose description of the application.
92    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub description: Option<String>,
95    /// > A URL to the Terms of Service for the API.
96    /// > MUST be in the format of a URL.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub terms_of_service: Option<Url>,
99    /// > The contact information for the exposed API.
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub contact: Option<Contact>,
102    /// > The license information for the exposed API.
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub license: Option<License>,
105    /// > REQUIRED.
106    /// > The version of the OpenRPC document
107    /// > (which is distinct from the OpenRPC Specification version or the API implementation version).
108    pub version: String,
109    #[serde(flatten)]
110    pub extensions: SpecificationExtensions,
111}
112
113/// > Contact information for the exposed API.
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
115pub struct Contact {
116    /// > The identifying name of the contact person/organization.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub name: Option<String>,
119    /// > The URL pointing to the contact information.
120    /// > MUST be in the format of a URL.
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub url: Option<Url>,
123    /// > The email address of the contact person/organization.
124    /// > MUST be in the format of an email address.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub email: Option<String>,
127    #[serde(flatten)]
128    pub extensions: SpecificationExtensions,
129}
130
131/// > License information for the exposed API.
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
133pub struct License {
134    /// > REQUIRED.
135    /// > The license name used for the API.
136    pub name: String,
137    /// > A URL to the license used for the API.
138    /// > MUST be in the format of a URL.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub url: Option<Url>,
141    #[serde(flatten)]
142    pub extensions: SpecificationExtensions,
143}
144
145/// > An object representing a Server.
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
147pub struct Server {
148    /// > REQUIRED.
149    /// > A name to be used as the cannonical name for the server.
150    pub name: String,
151    /// > REQUIRED.
152    /// > A URL to the target host.
153    /// > This URL supports Server Variables and MAY be relative,
154    /// > to indicate that the host location is relative to the location where the OpenRPC document is being served.
155    /// > Server Variables are passed into the Runtime Expression to produce a server URL.
156    pub url: String,
157    /// > A short summary of what the server is.
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub summary: Option<String>,
160    /// > An optional string describing the host designated by the URL.
161    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub description: Option<String>,
164    /// > A map between a variable name and its value.
165    /// > The value is passed into the Runtime Expression to produce a server URL.
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub variables: Option<BTreeMap<String, ServerVariable>>,
168    #[serde(flatten)]
169    pub extensions: SpecificationExtensions,
170}
171/// > An object representing a Server Variable for server URL template substitution.
172#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
173pub struct ServerVariable {
174    /// > An enumeration of string values to be used if the substitution options are from a limited set.
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub r#enum: Option<Vec<String>>,
177    /// > REQUIRED.
178    /// > The default value to use for substitution,
179    /// > which SHALL be sent if an alternate value is not supplied.
180    /// > Note this behavior is different than the Schema Object’s treatment of default values,
181    /// > because in those cases parameter values are optional.
182    pub default: String,
183    /// > An optional description for the server variable. GitHub Flavored Markdown syntax MAY be used for rich text representation.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub description: Option<String>,
186    #[serde(flatten)]
187    pub extensions: SpecificationExtensions,
188}
189
190/// > Describes the interface for the given method name.
191/// > The method name is used as the method field of the JSON-RPC body.
192/// > It therefore MUST be unique.
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
194#[serde(rename_all = "camelCase")]
195pub struct Method {
196    /// > REQUIRED.
197    /// > The cannonical name for the method.
198    /// > The name MUST be unique within the methods array.
199    pub name: String,
200    /// > A list of tags for API documentation control.
201    /// > Tags can be used for logical grouping of methods by resources or any other qualifier.
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub tags: Option<Vec<ReferenceOr<Tag>>>,
204    /// > A short summary of what the method does.
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub summary: Option<String>,
207    /// > A verbose explanation of the method behavior.
208    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub description: Option<String>,
211    /// > Additional external documentation for this method.
212    #[serde(skip_serializing_if = "Option::is_none")]
213    pub external_docs: Option<ExternalDocumentation>,
214    /// > REQUIRED.
215    /// > A list of parameters that are applicable for this method.
216    /// > The list MUST NOT include duplicated parameters and therefore require name to be unique.
217    /// > The list can use the Reference Object to link to parameters that are defined by the Content Descriptor Object.
218    /// > All optional params (content descriptor objects with “required”: false) MUST be positioned after all required params in the list.
219    pub params: Vec<ReferenceOr<ContentDescriptor>>,
220    /// > The description of the result returned by the method.
221    /// > If defined, it MUST be a Content Descriptor or Reference Object.
222    /// > If undefined, the method MUST only be used as a notification.
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub result: Option<ReferenceOr<ContentDescriptor>>,
225    /// > Declares this method to be deprecated.
226    /// > Consumers SHOULD refrain from usage of the declared method.
227    /// > Default value is `false`.
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub deprecated: Option<bool>,
230    /// > An alternative servers array to service this method.
231    /// > If an alternative servers array is specified at the Root level,
232    /// > it will be overridden by this value.
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub servers: Option<Vec<Server>>,
235    /// > A list of custom application defined errors that MAY be returned.
236    /// > The Errors MUST have unique error codes.
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub errors: Option<Vec<ReferenceOr<Error>>>,
239    // /// > A list of possible links from this method call.
240    // pub links: Option<Vec<ReferenceOr<Link>>>,
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub param_structure: Option<ParamStructure>,
243    /// > Array of Example Pairing Objects where each example includes a valid params-to-result Content Descriptor pairing.
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub examples: Option<Vec<ReferenceOr<ExamplePairing>>>,
246    #[serde(flatten)]
247    pub extensions: SpecificationExtensions,
248}
249
250#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251pub struct ContentDescriptor {
252    /// > REQUIRED.
253    /// > Name of the content that is being described.
254    /// > If the content described is a method parameter assignable by-name, this field SHALL define the parameter’s key (ie name).
255    pub name: String,
256    /// > A short summary of the content that is being described.
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub summary: Option<String>,
259    /// > A verbose explanation of the content descriptor behavior.
260    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub description: Option<String>,
263    /// > Determines if the content is a required field.
264    /// > Default value is `false`.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub required: Option<bool>,
267    /// > REQUIRED.
268    /// > Schema that describes the content.
269    ///
270    /// > The Schema Object allows the definition of input and output data types.
271    /// > The Schema Objects MUST follow the specifications outline in the JSON Schema Specification 7 Alternatively,
272    /// > any time a Schema Object can be used, a Reference Object can be used in its place.
273    /// > This allows referencing definitions instead of defining them inline.
274    ///
275    /// > This object MAY be extended with Specification Extensions.
276    pub schema: Schema,
277    /// > Specifies that the content is deprecated and SHOULD be transitioned out of usage.
278    /// > Default value is `false`.
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub deprecated: Option<bool>,
281    #[serde(flatten)]
282    pub extensions: SpecificationExtensions,
283}
284
285impl Default for ContentDescriptor {
286    fn default() -> Self {
287        Self {
288            name: Default::default(),
289            summary: Default::default(),
290            description: Default::default(),
291            required: Default::default(),
292            schema: schemars::json_schema!(false),
293            deprecated: Default::default(),
294            extensions: Default::default(),
295        }
296    }
297}
298
299/// > The Example Pairing object consists of a set of example params and result.
300/// > The result is what you can expect from the JSON-RPC service given the exact params.
301#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
302pub struct ExamplePairing {
303    /// > REQUIRED Name for the example pairing.
304    pub name: String,
305    /// > A verbose explanation of the example pairing.
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub description: Option<String>,
308    /// > Short description for the example pairing.
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub summary: Option<String>,
311    /// > REQUIRED Example parameters.
312    pub params: Vec<ReferenceOr<Example>>,
313    /// > Example result.
314    /// > When undefined, the example pairing represents usage of the method as a notification.
315    #[serde(skip_serializing_if = "Option::is_none")]
316    pub result: Option<ReferenceOr<Example>>,
317    #[serde(flatten)]
318    pub extensions: SpecificationExtensions,
319}
320
321/// > The Example object is an object that defines an example that is intended to match the schema of a given Content Descriptor.
322/// >
323/// > In all cases, the example value is expected to be compatible with the type schema of its associated value.
324/// > Tooling implementations MAY choose to validate compatibility automatically,
325/// > and reject the example value(s) if incompatible.
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327#[serde(rename_all = "camelCase")]
328pub struct Example {
329    /// Cannonical name of the example.
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub name: Option<String>,
332    /// Short description for the example.
333    #[serde(skip_serializing_if = "Option::is_none")]
334    pub summary: Option<String>,
335    /// > A verbose explanation of the example.
336    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
337    #[serde(skip_serializing_if = "Option::is_none")]
338    pub description: Option<String>,
339    #[serde(flatten)]
340    pub value: ExampleValue,
341    #[serde(flatten)]
342    pub extensions: SpecificationExtensions,
343}
344
345#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
346pub enum ExampleValue {
347    /// > A URL that points to the literal example.
348    /// > This provides the capability to reference examples that cannot easily be included in JSON documents.
349    /// > The value field and externalValue field are mutually exclusive.
350    #[serde(rename = "externalValue")]
351    External(String),
352    /// > Embedded literal example.
353    /// > The value field and externalValue field are mutually exclusive.
354    /// > To represent examples of media types that cannot naturally represented in JSON,
355    /// > use a string value to contain the example, escaping where necessary.
356    #[serde(rename = "value")]
357    Embedded(Value),
358}
359
360#[test]
361fn example() {
362    let json = serde_json::json!({
363        "name": "foo",
364        "value": { "foo": "bar" },
365        "x-tension": "extension"
366    });
367    let actual = serde_json::from_value::<Example>(json.clone()).unwrap();
368    assert_eq!(serde_json::to_value(actual).unwrap(), json);
369}
370
371// pub struct Link {} // TODO
372
373/// > Defines an application level error.
374#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
375pub struct Error {
376    /// > REQUIRED.
377    /// > A Number that indicates the error type that occurred.
378    /// > This MUST be an integer.
379    /// > The error codes from and including -32768 to -32000 are reserved for pre-defined errors.
380    /// > These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.
381    pub code: i64,
382    /// > REQUIRED.
383    /// > A String providing a short description of the error.
384    /// > The message SHOULD be limited to a concise single sentence.
385    pub message: String,
386    /// > A Primitive or Structured value that contains additional information about the error.
387    /// > This may be omitted.
388    /// > The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.).
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub data: Option<Value>,
391}
392
393/// > Holds a set of reusable objects for different aspects of the OpenRPC.
394/// > All objects defined within the components object will have no effect on the
395/// > API unless they are explicitly referenced from properties outside the components object.
396/// > All the fixed fields declared above are objects that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$
397#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
398#[serde(rename_all = "camelCase")]
399pub struct Components {
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub content_descriptors: Option<BTreeMap<String, ContentDescriptor>>,
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub schemas: Option<BTreeMap<String, Schema>>,
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub examples: Option<BTreeMap<String, Example>>,
406    // #[serde(skip_serializing_if = "Option::is_none")]
407    // pub links: Option<BTreeMap<String, Link>>, // TODO
408    #[serde(skip_serializing_if = "Option::is_none")]
409    pub errors: Option<BTreeMap<String, Error>>,
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub example_pairing_objects: Option<BTreeMap<String, ExamplePairing>>,
412    #[serde(skip_serializing_if = "Option::is_none")]
413    pub tags: Option<BTreeMap<String, Tag>>,
414    #[serde(flatten)]
415    pub extensions: SpecificationExtensions,
416}
417
418/// > Adds metadata to a single tag that is used by the Method Object.
419/// > It is not mandatory to have a Tag Object per tag defined in the Method Object instances.
420#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
421#[serde(rename_all = "camelCase")]
422pub struct Tag {
423    /// > REQUIRED.
424    /// > The name of the tag.
425    pub name: String,
426    /// > A short summary of the tag.
427    #[serde(skip_serializing_if = "Option::is_none")]
428    pub summary: Option<String>,
429    /// > A verbose explanation for the tag.
430    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
431    #[serde(skip_serializing_if = "Option::is_none")]
432    pub description: Option<String>,
433    /// > Additional external documentation for this tag.
434    #[serde(skip_serializing_if = "Option::is_none")]
435    pub external_docs: Option<ExternalDocumentation>,
436    #[serde(flatten)]
437    pub extensions: SpecificationExtensions,
438}
439
440/// > Allows referencing an external resource for extended documentation.
441#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442pub struct ExternalDocumentation {
443    /// > A verbose explanation of the target documentation.
444    /// > GitHub Flavored Markdown syntax MAY be used for rich text representation.
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub description: Option<String>,
447    /// > The URL for the target documentation.
448    /// > Value MUST be in the format of a URL.
449    pub url: Url,
450    #[serde(flatten)]
451    pub extensions: SpecificationExtensions,
452}
453
454#[derive(Debug, Clone, PartialEq)]
455pub enum ReferenceOr<T> {
456    /// > A simple object to allow referencing other components in the specification, internally and externally.
457    /// > The Reference Object is defined by JSON Schema and follows the same structure, behavior and rules.
458    Reference(String),
459    Item(T),
460}
461
462/// > While the OpenRPC Specification tries to accommodate most use cases,
463/// > additional data can be added to extend the specification at certain points.
464/// >
465/// > The extensions properties are implemented as patterned fields that are always prefixed by "x-".
466/// >
467/// > The extensions may or may not be supported by the available tooling,
468/// > but those may be extended as well to add requested support
469/// > (if tools are internal or open-sourced).
470#[derive(Debug, Clone, PartialEq, Serialize, Default)]
471#[serde(transparent)]
472pub struct SpecificationExtensions(pub BTreeMap<String, Value>);
473
474impl<'de> Deserialize<'de> for SpecificationExtensions {
475    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
476        struct Visitor;
477
478        impl<'de> serde::de::Visitor<'de> for Visitor {
479            type Value = BTreeMap<String, Value>;
480
481            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
482                formatter.write_str("a map with string keys starting with `x-`")
483            }
484
485            fn visit_map<A: serde::de::MapAccess<'de>>(
486                self,
487                mut map: A,
488            ) -> Result<Self::Value, A::Error> {
489                let mut ret = Self::Value::default();
490                loop {
491                    match map.next_key::<String>() {
492                        Err(_) => (),
493                        Ok(None) => break,
494                        Ok(Some(key)) if key.starts_with("x-") => {
495                            let _ = ret.insert(key, map.next_value()?);
496                        }
497                        Ok(Some(_)) => {
498                            let _ = map.next_value::<serde::de::IgnoredAny>()?;
499                        }
500                    }
501                }
502
503                Ok(ret)
504            }
505        }
506        deserializer.deserialize_any(Visitor).map(Self)
507    }
508}
509
510/// > The expected format of the parameters.
511/// > As per the JSON-RPC 2.0 specification,
512/// > the params of a JSON-RPC request object may be an array, object, or either
513/// > (represented as by-position, by-name, and either respectively).
514/// > When a method has a paramStructure value of by-name,
515/// > callers of the method MUST send a JSON-RPC request object whose params field is an object.
516/// > Further, the key names of the params object MUST be the same as the contentDescriptor.names for the given method.
517/// > Defaults to "either".
518#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
519#[serde(rename_all = "kebab-case")]
520pub enum ParamStructure {
521    ByName,
522    ByPosition,
523    #[default]
524    Either,
525}
526
527#[derive(Serialize, Deserialize)]
528#[serde(untagged, expecting = "a reference or an inline item")]
529enum _ReferenceOr<T> {
530    Reference {
531        #[serde(rename = "$ref")]
532        reference: String,
533    },
534    Item(T),
535}
536
537impl<T> ReferenceOr<T> {
538    pub fn map_item<U>(self, f: impl FnOnce(T) -> U) -> ReferenceOr<U> {
539        match self {
540            ReferenceOr::Reference(it) => ReferenceOr::Reference(it),
541            ReferenceOr::Item(it) => ReferenceOr::Item(f(it)),
542        }
543    }
544}
545
546impl<T: Serialize> Serialize for ReferenceOr<T> {
547    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
548        match self {
549            ReferenceOr::Reference(it) => _ReferenceOr::Reference {
550                reference: it.clone(),
551            },
552            ReferenceOr::Item(it) => _ReferenceOr::Item(it),
553        }
554        .serialize(serializer)
555    }
556}
557
558impl<'de, T: Deserialize<'de>> Deserialize<'de> for ReferenceOr<T> {
559    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
560        _ReferenceOr::deserialize(deserializer).map(|it| match it {
561            _ReferenceOr::Reference { reference } => ReferenceOr::Reference(reference),
562            _ReferenceOr::Item(it) => ReferenceOr::Item(it),
563        })
564    }
565}
566
567impl Default for resolved::OpenRPC {
568    fn default() -> Self {
569        Self {
570            openrpc: OPEN_RPC_SPECIFICATION_VERSION,
571            info: Default::default(),
572            servers: Default::default(),
573            methods: Default::default(),
574            components: Default::default(),
575            external_docs: Default::default(),
576            extensions: Default::default(),
577        }
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use super::*;
584    use regex::Regex;
585    use std::borrow::Cow;
586    use syn::{spanned::Spanned, Item};
587
588    // It's just easier this way
589    #[test]
590    fn generate_resolved() {
591        let lib = syn::parse_file(include_str!("lib.rs")).unwrap();
592        let regex = Regex::new("ReferenceOr<(?<item>[[:alnum:]]+?)>").unwrap();
593        let mut rewritten = String::from(
594            "\
595// This file is @generated by library tests
596
597//! Parallel types where [`ReferenceOr<T>`] is replaced by item `T`.
598
599use crate::*;
600use semver::Version;
601use serde::{Deserialize, Serialize};
602
603",
604        );
605
606        for item in lib.items {
607            if let Item::Struct(strukt) = item {
608                let source = strukt.span().source_text().unwrap();
609                match regex.replace_all(&source, "$item") {
610                    Cow::Borrowed(_) => {}
611                    Cow::Owned(replaced) => {
612                        rewritten.push_str(&replaced);
613                        rewritten.push('\n');
614                    }
615                }
616            }
617        }
618        expect_test::expect_file!["./resolved.rs"].assert_eq(&rewritten);
619    }
620
621    #[test]
622    fn test_default_content_desc_ser() {
623        let desc = ContentDescriptor::default();
624        println!("{}", serde_json::to_string_pretty(&desc).unwrap());
625        assert_eq!(
626            serde_json::to_value(&desc).unwrap(),
627            serde_json::json!({
628              "name": "",
629              "schema": false
630            })
631        );
632    }
633}