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}