1use schemars::gen::SchemaSettings;
2use schemars::schema::RootSchema;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7extern crate serde_json;
8
9#[derive(Serialize, Deserialize, Clone)]
10pub enum Openrpc {
11 #[serde(rename = "1.2.6")]
12 V26,
13 #[serde(rename = "1.2.5")]
14 V25,
15 #[serde(rename = "1.2.4")]
16 V24,
17 #[serde(rename = "1.2.3")]
18 V23,
19 #[serde(rename = "1.2.2")]
20 V22,
21 #[serde(rename = "1.2.1")]
22 V21,
23 #[serde(rename = "1.2.0")]
24 V20,
25 #[serde(rename = "1.1.12")]
26 V112,
27 #[serde(rename = "1.1.11")]
28 V111,
29 #[serde(rename = "1.1.10")]
30 V110,
31 #[serde(rename = "1.1.9")]
32 V19,
33 #[serde(rename = "1.1.8")]
34 V18,
35 #[serde(rename = "1.1.7")]
36 V17,
37 #[serde(rename = "1.1.6")]
38 V16,
39 #[serde(rename = "1.1.5")]
40 V15,
41 #[serde(rename = "1.1.4")]
42 V14,
43 #[serde(rename = "1.1.3")]
44 V13,
45 #[serde(rename = "1.1.2")]
46 V12,
47 #[serde(rename = "1.1.1")]
48 V11,
49 #[serde(rename = "1.1.0")]
50 V10,
51 #[serde(rename = "1.0.0")]
52 V00,
53 #[serde(rename = "1.0.0-rc1")]
54 V00Rc1,
55 #[serde(rename = "1.0.0-rc0")]
56 V00Rc0,
57}
58
59pub type InfoObjectProperties = String;
60pub type InfoObjectDescription = String;
61pub type InfoObjectTermsOfService = String;
62pub type InfoObjectVersion = String;
63pub type ContactObjectName = String;
64pub type ContactObjectEmail = String;
65pub type ContactObjectUrl = String;
66pub type SpecificationExtension = serde_json::Value;
67
68#[derive(Serialize, Deserialize, Clone)]
69pub struct ContactObject {
70 pub name: Option<ContactObjectName>,
71 pub email: Option<ContactObjectEmail>,
72 pub url: Option<ContactObjectUrl>,
73}
74
75pub type LicenseObjectName = String;
76pub type LicenseObjectUrl = String;
77
78#[derive(Serialize, Deserialize, Clone)]
79pub struct LicenseObject {
80 pub name: Option<LicenseObjectName>,
81 pub url: Option<LicenseObjectUrl>,
82}
83
84#[derive(Serialize, Deserialize, Clone)]
85pub struct InfoObject {
86 pub title: InfoObjectProperties,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub description: Option<InfoObjectDescription>,
89 #[serde(rename = "termsOfService")]
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub terms_of_service: Option<InfoObjectTermsOfService>,
92 pub version: InfoObjectVersion,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub contact: Option<ContactObject>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub license: Option<LicenseObject>,
97}
98
99pub type ExternalDocumentationObjectDescription = String;
100pub type ExternalDocumentationObjectUrl = String;
101
102#[derive(Serialize, Deserialize, Clone)]
107pub struct ExternalDocumentationObject {
108 pub description: Option<ExternalDocumentationObjectDescription>,
109 pub url: ExternalDocumentationObjectUrl,
110}
111
112pub type ServerObjectUrl = String;
113pub type ServerObjectName = String;
114pub type ServerObjectDescription = String;
115pub type ServerObjectSummary = String;
116pub type ServerObjectVariableDefault = String;
117pub type ServerObjectVariableDescription = String;
118pub type ServerObjectVariableEnumItem = String;
119pub type ServerObjectVariableEnum = Vec<ServerObjectVariableEnumItem>;
120
121#[derive(Serialize, Deserialize, Clone)]
122pub struct ServerObjectVariable {
123 pub default: ServerObjectVariableDefault,
124 pub description: Option<ServerObjectVariableDescription>,
125 #[serde(rename = "enum")]
126 pub variable_enum: Option<ServerObjectVariableEnum>,
127}
128
129pub type ServerObjectVariables = HashMap<String, Option<serde_json::Value>>;
130
131#[derive(Serialize, Deserialize, Clone)]
132pub struct ServerObject {
133 pub url: ServerObjectUrl,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub name: Option<ServerObjectName>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub description: Option<ServerObjectDescription>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub summary: Option<ServerObjectSummary>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub variables: Option<ServerObjectVariables>,
142}
143
144pub type Servers = Vec<ServerObject>;
145pub type MethodObjectName = String;
150pub type MethodObjectDescription = String;
155pub type MethodObjectSummary = String;
160pub type TagObjectName = String;
161pub type TagObjectDescription = String;
162
163#[derive(Serialize, Deserialize, Clone)]
164pub struct TagObject {
165 pub name: TagObjectName,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub description: Option<TagObjectDescription>,
168 #[serde(rename = "externalDocs")]
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub external_docs: Option<ExternalDocumentationObject>,
171}
172
173#[derive(Serialize, Deserialize, Clone)]
174pub struct ReferenceObject {
175 #[serde(rename = "$ref")]
176 pub reference: String,
177}
178
179#[derive(Serialize, Deserialize, Clone)]
180#[serde(untagged)]
181pub enum TagOrReference {
182 TagObject(TagObject),
183 ReferenceObject(ReferenceObject),
184}
185
186pub type MethodObjectTags = Vec<TagOrReference>;
187
188#[derive(Serialize, Deserialize, Clone)]
197pub enum MethodObjectParamStructure {
198 #[serde(rename = "by-position")]
199 ByPosition,
200 #[serde(rename = "by-name")]
201 ByName,
202 #[serde(rename = "either")]
203 Either,
204}
205
206pub type ContentDescriptorObjectName = String;
207pub type ContentDescriptorObjectDescription = String;
208pub type ContentDescriptorObjectSummary = String;
209pub type Id = String;
210pub type Schema = String;
211pub type Comment = String;
212pub type Title = String;
213pub type Description = String;
214type AlwaysTrue = serde_json::Value;
215pub type ReadOnly = bool;
216pub type Examples = Vec<AlwaysTrue>;
217pub type MultipleOf = f64;
218pub type Maximum = f64;
219pub type ExclusiveMaximum = f64;
220pub type Minimum = f64;
221pub type ExclusiveMinimum = f64;
222pub type NonNegativeInteger = i64;
223pub type NonNegativeIntegerDefaultZero = i64;
224pub type Pattern = String;
225pub type SchemaArray = Vec<JSONSchema>;
226
227#[derive(Serialize, Deserialize, Clone)]
228#[serde(untagged)]
229pub enum Items {
230 JSONSchema(JSONSchema),
231 SchemaArray(SchemaArray),
232}
233
234pub type UniqueItems = bool;
235pub type StringDoaGddGA = String;
236pub type StringArray = Vec<StringDoaGddGA>;
243pub type Definitions = HashMap<String, Option<serde_json::Value>>;
250pub type Properties = HashMap<String, Option<serde_json::Value>>;
257pub type PatternProperties = HashMap<String, Option<serde_json::Value>>;
264
265#[derive(Serialize, Deserialize, Clone)]
266#[serde(untagged)]
267pub enum DependenciesSet {
268 JSONSchema(JSONSchema),
269 StringArray(StringArray),
270}
271
272pub type Dependencies = HashMap<String, Option<serde_json::Value>>;
273pub type Enum = Vec<AlwaysTrue>;
274pub type SimpleTypes = serde_json::Value;
275pub type ArrayOfSimpleTypes = Vec<SimpleTypes>;
276
277#[derive(Serialize, Deserialize, Clone)]
278#[serde(untagged)]
279pub enum Type {
280 SimpleTypes(SimpleTypes),
281 ArrayOfSimpleTypes(ArrayOfSimpleTypes),
282}
283
284pub type Format = String;
285pub type ContentMediaType = String;
286pub type ContentEncoding = String;
287
288#[derive(Serialize, Deserialize, Clone)]
293#[serde(untagged)]
294pub enum JSONSchema {
295 JsonSchemaObject(RootSchema),
296 JSONSchemaBoolean(bool),
297}
298
299pub type ContentDescriptorObjectRequired = bool;
300pub type ContentDescriptorObjectDeprecated = bool;
301
302#[derive(Serialize, Deserialize, Clone)]
303pub struct ContentDescriptorObject {
304 pub name: ContentDescriptorObjectName,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub description: Option<ContentDescriptorObjectDescription>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub summary: Option<ContentDescriptorObjectSummary>,
309 pub schema: JSONSchema,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub required: Option<ContentDescriptorObjectRequired>,
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub deprecated: Option<ContentDescriptorObjectDeprecated>,
314}
315
316#[derive(Serialize, Deserialize, Clone)]
317#[serde(untagged)]
318pub enum ContentDescriptorOrReference {
319 ContentDescriptorObject(ContentDescriptorObject),
320 ReferenceObject(ReferenceObject),
321}
322
323pub type MethodObjectParams = Vec<ContentDescriptorOrReference>;
324
325pub type ErrorObjectCode = i64;
330pub type ErrorObjectMessage = String;
335pub type ErrorObjectData = serde_json::Value;
340
341#[derive(Serialize, Deserialize, Clone)]
346pub struct ErrorObject {
347 pub code: ErrorObjectCode,
348 pub message: ErrorObjectMessage,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub data: Option<ErrorObjectData>,
351}
352
353#[derive(Serialize, Deserialize, Clone)]
354#[serde(untagged)]
355pub enum ErrorOrReference {
356 ErrorObject(ErrorObject),
357 ReferenceObject(ReferenceObject),
358}
359
360pub type MethodObjectErrors = Vec<ErrorOrReference>;
365pub type LinkObjectName = String;
366pub type LinkObjectSummary = String;
367pub type LinkObjectMethod = String;
368pub type LinkObjectDescription = String;
369pub type LinkObjectParams = serde_json::Value;
370
371#[derive(Serialize, Deserialize, Clone)]
372pub struct LinkObjectServer {
373 pub url: ServerObjectUrl,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub name: Option<ServerObjectName>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub description: Option<ServerObjectDescription>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub summary: Option<ServerObjectSummary>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub variables: Option<ServerObjectVariables>,
382}
383
384#[derive(Serialize, Deserialize, Clone)]
385pub struct LinkObject {
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub name: Option<LinkObjectName>,
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub summary: Option<LinkObjectSummary>,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub method: Option<LinkObjectMethod>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub description: Option<LinkObjectDescription>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub params: Option<LinkObjectParams>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub server: Option<LinkObjectServer>,
398}
399
400#[derive(Serialize, Deserialize, Clone)]
401#[serde(untagged)]
402pub enum LinkOrReference {
403 LinkObject(LinkObject),
404 ReferenceObject(ReferenceObject),
405}
406
407pub type MethodObjectLinks = Vec<LinkOrReference>;
408pub type ExamplePairingObjectName = String;
409pub type ExamplePairingObjectDescription = String;
410pub type ExampleObjectSummary = String;
411pub type ExampleObjectValue = serde_json::Value;
412pub type ExampleObjectDescription = String;
413pub type ExampleObjectName = String;
414
415#[derive(Serialize, Deserialize, Clone)]
416pub struct ExampleObject {
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub summary: Option<ExampleObjectSummary>,
419 pub value: ExampleObjectValue,
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub description: Option<ExampleObjectDescription>,
422 pub name: ExampleObjectName,
423}
424
425#[derive(Serialize, Deserialize, Clone)]
426#[serde(untagged)]
427pub enum ExampleOrReference {
428 ExampleObject(ExampleObject),
429 ReferenceObject(ReferenceObject),
430}
431
432pub type ExamplePairingObjectParams = Vec<ExampleOrReference>;
433
434#[derive(Serialize, Deserialize, Clone)]
435#[serde(untagged)]
436pub enum ExamplePairingObjectResult {
437 ExampleObject(ExampleObject),
438 ReferenceObject(ReferenceObject),
439}
440
441#[derive(Serialize, Deserialize, Clone)]
442pub struct ExamplePairingObject {
443 pub name: ExamplePairingObjectName,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub description: Option<ExamplePairingObjectDescription>,
446 pub params: ExamplePairingObjectParams,
447 pub result: ExamplePairingObjectResult,
448}
449
450#[derive(Serialize, Deserialize, Clone)]
451#[serde(untagged)]
452pub enum ExamplePairingOrReference {
453 ExampleObject(ExampleObject),
454 ReferenceObject(ReferenceObject),
455}
456
457pub type MethodObjectExamples = Vec<ExamplePairingOrReference>;
458pub type MethodObjectDeprecated = bool;
459
460#[derive(Serialize, Deserialize, Clone)]
461pub struct MethodObject {
462 pub name: MethodObjectName,
463 #[serde(skip_serializing_if = "Option::is_none")]
464 pub description: Option<MethodObjectDescription>,
465 #[serde(skip_serializing_if = "Option::is_none")]
466 pub summary: Option<MethodObjectSummary>,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 pub servers: Option<Servers>,
469 #[serde(skip_serializing_if = "Option::is_none")]
470 pub tags: Option<MethodObjectTags>,
471 #[serde(rename = "paramStructure")]
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub param_structure: Option<MethodObjectParamStructure>,
474 pub params: MethodObjectParams,
475 pub result: ContentDescriptorOrReference,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub errors: Option<MethodObjectErrors>,
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub links: Option<MethodObjectLinks>,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub examples: Option<MethodObjectExamples>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub deprecated: Option<MethodObjectDeprecated>,
484 #[serde(skip_serializing_if = "Option::is_none")]
485 #[serde(rename = "externalDocs")]
486 pub external_docs: Option<ExternalDocumentationObject>,
487}
488
489pub type Methods = Vec<MethodObject>;
490pub type SchemaComponents = HashMap<String, Option<serde_json::Value>>;
491pub type LinkComponents = HashMap<String, Option<serde_json::Value>>;
492pub type ErrorComponents = HashMap<String, Option<serde_json::Value>>;
493pub type ExampleComponents = HashMap<String, Option<serde_json::Value>>;
494pub type ExamplePairingComponents = HashMap<String, Option<serde_json::Value>>;
495pub type ContentDescriptorComponents = HashMap<String, Option<serde_json::Value>>;
496pub type TagComponents = HashMap<String, Option<serde_json::Value>>;
497
498#[derive(Serialize, Deserialize, Clone)]
499pub struct Components {
500 #[serde(skip_serializing_if = "Option::is_none")]
501 pub schemas: Option<SchemaComponents>,
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub links: Option<LinkComponents>,
504 #[serde(skip_serializing_if = "Option::is_none")]
505 pub errors: Option<ErrorComponents>,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub examples: Option<ExampleComponents>,
508 #[serde(rename = "examplePairings")]
509 pub example_pairings: Option<ExamplePairingComponents>,
510 #[serde(rename = "contentDescriptors")]
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub content_descriptors: Option<ContentDescriptorComponents>,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub tags: Option<TagComponents>,
515}
516
517#[derive(Serialize, Deserialize, Clone)]
518pub struct OpenrpcDocument {
519 pub openrpc: Openrpc,
520 pub info: InfoObject,
521 #[serde(rename = "externalDocs")]
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub external_docs: Option<ExternalDocumentationObject>,
524 #[serde(skip_serializing_if = "Option::is_none")]
525 pub servers: Option<Servers>,
526 pub methods: Methods,
527 #[serde(skip_serializing_if = "Option::is_none")]
528 pub components: Option<Components>,
529}
530
531impl Default for OpenrpcDocument {
532 fn default() -> Self {
533 OpenrpcDocument {
534 openrpc: Openrpc::V26,
535 info: InfoObject {
536 title: "".to_string(),
537 description: None,
538 terms_of_service: None,
539 version: "".to_string(),
540 contact: None,
541 license: None,
542 },
543 external_docs: None,
544 servers: None,
545 methods: vec![],
546 components: None,
547 }
548 }
549}
550
551impl OpenrpcDocument {
552 pub fn set_info(mut self, info: InfoObject) -> Self {
553 self.info = info;
554 self
555 }
556 pub fn add_object_method(&mut self, method: MethodObject) {
557 self.methods.push(method)
558 }
559}
560
561impl ContentDescriptorOrReference {
562 pub fn new_content_descriptor<T: ?Sized + JsonSchema>(
563 name: ContactObjectName,
564 description: Option<Description>,
565 ) -> Self {
566 let mut setting = SchemaSettings::draft07();
567 setting.inline_subschemas = true;
568 let schema = schemars::gen::SchemaGenerator::new(setting).into_root_schema_for::<T>();
569 let json_schema = JSONSchema::JsonSchemaObject(schema);
570 ContentDescriptorOrReference::ContentDescriptorObject(ContentDescriptorObject {
571 name,
572 description,
573 summary: None,
574 schema: json_schema,
575 required: None,
576 deprecated: None,
577 })
578 }
579}
580
581impl MethodObject {
582 pub fn new(name: MethodObjectName, description: Option<Description>) -> Self {
583 Self {
584 name,
585 description,
586 summary: None,
587 servers: None,
588 tags: None,
589 param_structure: None,
590 params: vec![],
591 result: ContentDescriptorOrReference::ReferenceObject(ReferenceObject {
592 reference: "".to_string(),
593 }),
594 errors: None,
595 links: None,
596 examples: None,
597 deprecated: None,
598 external_docs: None,
599 }
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606 #[derive(JsonSchema)]
607 pub struct MyType([u8; 8]);
608
609 #[derive(JsonSchema)]
610 pub struct MyParam {
611 pub my_int: i32,
612 pub my_bool: bool,
613 pub my_type: Box<MyType>,
614 }
615
616 #[derive(JsonSchema)]
617 pub struct MyRet {
618 pub success: Box<bool>,
619 }
620
621 #[test]
622 fn test_openrpc_document() {
623 let mut document = OpenrpcDocument::default();
624 let mut method = MethodObject::new("method1".to_string(), None);
625 let param = ContentDescriptorOrReference::new_content_descriptor::<MyParam>(
626 "first_param".to_string(),
627 Some("no desc".to_string()),
628 );
629 method.params.push(param);
630 method.result =
631 ContentDescriptorOrReference::new_content_descriptor::<MyRet>("ret".to_string(), None);
632 document.add_object_method(method);
633 let j = serde_json::to_string_pretty(&document).unwrap();
634 println!("{}", j);
635 }
636}