Skip to main content

gws_builder/
discovery.rs

1//! Serde models for Google Discovery REST documents (owned, no lifetimes).
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7/// Top-level Discovery REST description document.
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9#[serde(rename_all = "camelCase")]
10pub struct RestDescription {
11    pub name: String,
12    pub version: String,
13    pub title: Option<String>,
14    pub description: Option<String>,
15    /// Human-readable canonical API name (when present).
16    pub canonical_name: Option<String>,
17    /// Document revision string (often `YYYYMMDD`).
18    pub revision: Option<String>,
19    pub root_url: String,
20    #[serde(default)]
21    pub service_path: String,
22    pub base_url: Option<String>,
23    #[serde(default)]
24    pub schemas: HashMap<String, JsonSchema>,
25    #[serde(default)]
26    pub resources: HashMap<String, RestResource>,
27    #[serde(default)]
28    pub parameters: HashMap<String, MethodParameter>,
29    pub auth: Option<AuthDescription>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, Default)]
33pub struct AuthDescription {
34    pub oauth2: Option<OAuth2Description>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, Default)]
38pub struct OAuth2Description {
39    pub scopes: Option<HashMap<String, ScopeDescription>>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, Default)]
43pub struct ScopeDescription {
44    pub description: Option<String>,
45}
46
47/// A resource tree node: methods and nested sub-resources.
48#[derive(Debug, Clone, Serialize, Deserialize, Default)]
49pub struct RestResource {
50    #[serde(default)]
51    pub methods: HashMap<String, RestMethod>,
52    #[serde(default)]
53    pub resources: HashMap<String, RestResource>,
54}
55
56/// A single REST method on a resource.
57#[derive(Debug, Clone, Serialize, Deserialize, Default)]
58#[serde(rename_all = "camelCase")]
59pub struct RestMethod {
60    pub id: Option<String>,
61    pub description: Option<String>,
62    pub http_method: String,
63    pub path: String,
64    #[serde(default)]
65    pub parameters: HashMap<String, MethodParameter>,
66    #[serde(default)]
67    pub parameter_order: Vec<String>,
68    pub request: Option<SchemaRef>,
69    pub response: Option<SchemaRef>,
70    #[serde(default)]
71    pub scopes: Vec<String>,
72    pub flat_path: Option<String>,
73    #[serde(default)]
74    pub supports_media_download: bool,
75    #[serde(default)]
76    pub supports_media_upload: bool,
77    pub media_upload: Option<MediaUpload>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, Default)]
81pub struct MediaUpload {
82    pub protocols: Option<MediaUploadProtocols>,
83    #[serde(default)]
84    pub accept: Vec<String>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
88pub struct MediaUploadProtocols {
89    pub simple: Option<MediaUploadProtocol>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, Default)]
93pub struct MediaUploadProtocol {
94    pub path: String,
95    /// Discovery docs use a boolean for `multipart` (e.g. Drive).
96    pub multipart: Option<bool>,
97}
98
99/// Reference to another schema (`$ref`).
100#[derive(Debug, Clone, Serialize, Deserialize, Default)]
101pub struct SchemaRef {
102    #[serde(rename = "$ref")]
103    pub schema_ref: Option<String>,
104    #[serde(rename = "parameterName")]
105    pub parameter_name: Option<String>,
106}
107
108/// Method parameter (path, query, or body metadata).
109#[derive(Debug, Clone, Serialize, Deserialize, Default)]
110#[serde(rename_all = "camelCase")]
111pub struct MethodParameter {
112    #[serde(rename = "type")]
113    pub param_type: Option<String>,
114    pub description: Option<String>,
115    pub location: Option<String>,
116    #[serde(default)]
117    pub required: bool,
118    pub format: Option<String>,
119    pub default: Option<serde_json::Value>,
120    #[serde(rename = "enum")]
121    pub enum_values: Option<Vec<String>>,
122    pub enum_descriptions: Option<Vec<String>>,
123    #[serde(default)]
124    pub repeated: bool,
125    pub minimum: Option<String>,
126    pub maximum: Option<String>,
127    #[serde(default)]
128    pub deprecated: bool,
129}
130
131/// JSON Schema object used for Discovery `schemas` entries.
132#[derive(Debug, Clone, Serialize, Deserialize, Default)]
133#[serde(rename_all = "camelCase")]
134pub struct JsonSchema {
135    pub id: Option<String>,
136    #[serde(rename = "type")]
137    pub schema_type: Option<String>,
138    pub description: Option<String>,
139    #[serde(default)]
140    pub deprecated: bool,
141    #[serde(default)]
142    pub properties: HashMap<String, JsonSchemaProperty>,
143    #[serde(rename = "$ref")]
144    pub schema_ref: Option<String>,
145    pub items: Option<Box<JsonSchemaProperty>>,
146    #[serde(default)]
147    pub required: Vec<String>,
148    pub additional_properties: Option<serde_json::Value>,
149    #[serde(rename = "enum")]
150    pub enum_values: Option<Vec<String>>,
151    pub enum_descriptions: Option<Vec<String>>,
152}
153
154/// Property within a JSON Schema `properties` map.
155#[derive(Debug, Clone, Serialize, Deserialize, Default)]
156#[serde(rename_all = "camelCase")]
157pub struct JsonSchemaProperty {
158    #[serde(rename = "type")]
159    pub prop_type: Option<String>,
160    pub description: Option<String>,
161    #[serde(rename = "$ref")]
162    pub schema_ref: Option<String>,
163    pub format: Option<String>,
164    pub items: Option<Box<JsonSchemaProperty>>,
165    #[serde(default)]
166    pub properties: HashMap<String, JsonSchemaProperty>,
167    #[serde(default)]
168    pub read_only: bool,
169    pub default: Option<serde_json::Value>,
170    #[serde(rename = "enum")]
171    pub enum_values: Option<Vec<String>>,
172    pub enum_descriptions: Option<Vec<String>>,
173    pub additional_properties: Option<serde_json::Value>,
174    /// Extra annotations (e.g. Gmail `annotations.required`).
175    pub annotations: Option<serde_json::Value>,
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn deserialize_minimal_rest_description() {
184        let json = r#"{
185            "name": "drive",
186            "version": "v3",
187            "rootUrl": "https://www.googleapis.com/",
188            "servicePath": "drive/v3/",
189            "resources": {
190                "files": {
191                    "methods": {
192                        "list": {
193                            "httpMethod": "GET",
194                            "path": "files",
195                            "response": { "$ref": "FileList" }
196                        }
197                    }
198                }
199            },
200            "schemas": {
201                "FileList": {
202                    "id": "FileList",
203                    "type": "object",
204                    "properties": {
205                        "files": {
206                            "type": "array",
207                            "items": { "$ref": "File" }
208                        }
209                    }
210                }
211            }
212        }"#;
213
214        let doc: RestDescription = serde_json::from_str(json).expect("parse");
215        assert_eq!(doc.name, "drive");
216        assert_eq!(doc.version, "v3");
217        let files = doc.resources.get("files").expect("files");
218        let list = files.methods.get("list").expect("list");
219        assert_eq!(list.http_method, "GET");
220        assert_eq!(list.path, "files");
221    }
222}