1use serde::{Deserialize, Serialize};
5use serde_json::{Map, Value};
6
7pub use crate::options::OptionField;
8use crate::{Kind, PluginId};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
15#[schemars(rename = "{T}")]
17pub struct Envelope<T> {
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub id: Option<u64>,
20 #[serde(flatten)]
21 pub body: T,
22}
23
24impl<T> Envelope<T> {
25 pub fn new(id: Option<u64>, body: T) -> Envelope<T> {
26 Envelope { id, body }
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
32#[serde(tag = "type", rename_all = "snake_case")]
33pub enum Request {
34 Hello {
36 min_version: u32,
37 max_version: u32,
38 core_version: String,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
42 plugin: Option<PluginId>,
43 },
44 Describe {},
47 Validate {
50 options: Map<String, Value>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
55 template: Option<Value>,
56 },
57 ValidateConnection {
62 connection: Map<String, Value>,
63 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 unresolved: Vec<String>,
65 },
66 Open {
68 connection: Map<String, Value>,
69 read_only: bool,
70 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
73 sandbox: bool,
74 },
75 Execute {
78 sql: String,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 row_limit: Option<u64>,
81 },
82 Check {
84 sql: String,
85 },
86 Catalog {
88 #[serde(default)]
89 tables: Vec<String>,
90 #[serde(default)]
91 schemas: Vec<String>,
92 },
93 Write {
96 path: String,
97 format: String,
98 options: Map<String, Value>,
99 result_sets: Vec<ResultSetMeta>,
100 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
102 font_requests: bool,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 template: Option<Value>,
105 },
106 Fonts {
110 paths: Vec<String>,
111 error: Option<String>,
112 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
113 repeated: bool,
114 },
115 ResultSetEnd {},
116 Finish {},
117 Deliver {
123 #[serde(default, skip_serializing_if = "Option::is_none")]
124 local_path: Option<String>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 remote_path: Option<String>,
127 #[serde(default, skip_serializing_if = "Vec::is_empty")]
128 files: Vec<DeliveryFile>,
129 connection: Map<String, Value>,
130 #[serde(default)]
131 options: Map<String, Value>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 message: Option<Message>,
134 },
135 Load {
139 name: String,
140 },
141 Close {},
143 Cancel {},
147}
148
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
151#[serde(tag = "type", rename_all = "snake_case")]
152pub enum Response {
153 Hello {
154 protocol_version: u32,
155 kind: Kind,
156 name: String,
157 version: String,
158 #[serde(default)]
159 capabilities: Vec<String>,
160 #[serde(default, skip_serializing_if = "Vec::is_empty")]
162 provides: Vec<PluginId>,
163 },
164 VersionMismatch {
165 min_version: u32,
166 max_version: u32,
167 },
168 Describe {
169 connection_fields: Vec<ConnectionField>,
170 #[serde(default, skip_serializing_if = "Vec::is_empty")]
171 option_fields: Vec<OptionField>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
176 identifier_quote: Option<String>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
181 string_literal: Option<String>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
185 message_limit: Option<u64>,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
190 single_writer: Option<SingleWriter>,
191 },
192 Validated {
194 errors: Vec<String>,
195 #[serde(default, skip_serializing_if = "Vec::is_empty")]
197 warnings: Vec<String>,
198 },
199 Ok {},
200 Result {
202 columns: Vec<String>,
203 },
204 ResultEnd {
205 rows: u64,
206 },
207 NoResult {
208 #[serde(default, skip_serializing_if = "Option::is_none")]
209 rows_affected: Option<u64>,
210 },
211 Catalog {
213 catalog: Value,
214 },
215 NeedFonts {
217 font: FontRequest,
218 },
219 Written {
220 files: Vec<String>,
221 #[serde(default, skip_serializing_if = "Vec::is_empty")]
223 warnings: Vec<String>,
224 },
225 Delivered {
226 location: String,
227 #[serde(default, skip_serializing_if = "Option::is_none")]
229 attempts: Option<u32>,
230 },
231 Loaded {
234 relation: String,
235 rows: u64,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
237 warning: Option<String>,
238 },
239 Error {
244 message: String,
245 #[serde(default, skip_serializing_if = "Option::is_none")]
246 kind: Option<String>,
247 #[serde(default, skip_serializing_if = "Option::is_none")]
248 code: Option<String>,
249 },
250 Log {
252 level: LogLevel,
253 message: String,
254 #[serde(default, skip_serializing_if = "Map::is_empty")]
256 fields: Map<String, Value>,
257 },
258 Progress {
260 #[serde(default, skip_serializing_if = "Option::is_none")]
261 message: Option<String>,
262 #[serde(default, skip_serializing_if = "Option::is_none")]
263 done: Option<u64>,
264 #[serde(default, skip_serializing_if = "Option::is_none")]
265 total: Option<u64>,
266 },
267}
268
269impl Response {
270 pub fn error(message: impl Into<String>) -> Response {
272 Response::Error {
273 message: message.into(),
274 kind: None,
275 code: None,
276 }
277 }
278}
279
280pub const ERROR_KINDS: &[&str] = &[
282 "config",
283 "plugin",
284 "refused",
285 "connection",
286 "auth",
287 "query",
288 "delivery",
289 "internal",
290 "cancelled",
291 "timed_out",
292];
293
294#[derive(
297 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, schemars::JsonSchema,
298)]
299#[serde(rename_all = "lowercase")]
300pub enum LogLevel {
301 Error,
302 Warn,
303 Info,
304 Debug,
305 Trace,
306}
307
308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
310pub struct Message {
311 pub title: String,
313 pub text: String,
315 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub html: Option<String>,
319 pub path: String,
321}
322
323#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
325pub struct DeliveryFile {
326 pub local_path: String,
327 #[serde(default, skip_serializing_if = "Option::is_none")]
328 pub remote_path: Option<String>,
329}
330
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
333pub struct ResultSetMeta {
334 pub name: String,
336 pub query: String,
338 pub result_index: usize,
340 #[serde(default, skip_serializing_if = "Option::is_none")]
341 pub anchor: Option<String>,
342 #[serde(default, skip_serializing_if = "Option::is_none")]
343 pub header: Option<bool>,
344 #[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
346 pub columns: std::collections::BTreeMap<String, ColumnOptions>,
347 #[serde(default, skip_serializing_if = "Option::is_none")]
350 pub autofit: Option<bool>,
351 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub style: Option<crate::style::SheetStyle>,
354}
355
356#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
358pub struct ColumnOptions {
359 #[serde(default, skip_serializing_if = "Option::is_none")]
361 pub format: Option<String>,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub formula: Option<String>,
366 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub total: Option<String>,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
372 pub width: Option<ColumnWidth>,
373 #[serde(default, skip_serializing_if = "Option::is_none")]
375 pub style: Option<crate::style::CellStyle>,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
380#[serde(untagged)]
381pub enum ColumnWidth {
382 Chars(f64),
383 Auto(AutoWidth),
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
388#[serde(rename_all = "lowercase")]
389pub enum AutoWidth {
390 Auto,
391}
392
393#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
395pub struct SingleWriter {
396 pub field: String,
398 #[serde(default, skip_serializing_if = "Vec::is_empty")]
400 pub except: Vec<String>,
401}
402
403impl SingleWriter {
404 pub fn applies(&self, connection: &Map<String, Value>) -> bool {
406 connection
407 .get(&self.field)
408 .and_then(Value::as_str)
409 .is_some_and(|v| !self.except.iter().any(|e| e == v))
410 }
411}
412
413#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
414pub struct ConnectionField {
415 pub name: String,
416 #[serde(default)]
417 pub description: String,
418 #[serde(default)]
419 pub required: bool,
420 #[serde(default)]
422 pub secret: bool,
423 #[serde(default, skip_serializing_if = "Option::is_none")]
424 pub default: Option<Value>,
425 #[serde(default, skip_serializing_if = "Option::is_none")]
428 pub same_as_source: Option<String>,
429 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
432 pub manual: bool,
433 #[serde(default, skip_serializing_if = "Option::is_none")]
435 pub kind: Option<FieldKind>,
436 #[serde(default, skip_serializing_if = "Vec::is_empty")]
438 pub choices: Vec<String>,
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
443#[serde(rename_all = "snake_case")]
444pub enum FieldKind {
445 String,
446 Integer,
448 Boolean,
449 Path,
451 Duration,
453 Map,
455}
456
457impl ConnectionField {
458 pub fn new(name: &str, description: &str) -> Self {
459 ConnectionField {
460 name: name.into(),
461 description: description.into(),
462 required: false,
463 secret: false,
464 default: None,
465 same_as_source: None,
466 manual: false,
467 kind: None,
468 choices: Vec::new(),
469 }
470 }
471 #[must_use]
472 pub fn kind(mut self, kind: FieldKind) -> Self {
473 self.kind = Some(kind);
474 self
475 }
476 #[must_use]
477 pub fn choices(mut self, choices: &[&str]) -> Self {
478 self.choices = choices.iter().map(|c| c.to_string()).collect();
479 self.kind.get_or_insert(FieldKind::String);
480 self
481 }
482 #[must_use]
483 pub fn required(mut self) -> Self {
484 self.required = true;
485 self
486 }
487 #[must_use]
488 pub fn secret(mut self) -> Self {
489 self.secret = true;
490 self
491 }
492 #[must_use]
493 pub fn default(mut self, v: impl Into<Value>) -> Self {
494 self.default = Some(v.into());
495 self
496 }
497 #[must_use]
498 pub fn same_as_source(mut self, source_type: &str) -> Self {
499 self.same_as_source = Some(source_type.into());
500 self
501 }
502 #[must_use]
503 pub fn manual(mut self) -> Self {
504 self.manual = true;
505 self
506 }
507}
508
509#[cfg(test)]
510mod font_tests {
511 use super::*;
512
513 #[test]
514 fn old_write_requests_do_not_enable_font_requests() {
515 let request: Request = serde_json::from_value(serde_json::json!({
516 "type": "write", "path": "out.pdf", "format": "pdf", "options": {}, "result_sets": []
517 }))
518 .unwrap();
519 assert!(matches!(
520 request,
521 Request::Write {
522 font_requests: false,
523 ..
524 }
525 ));
526 assert!(
527 serde_json::to_value(request)
528 .unwrap()
529 .get("font_requests")
530 .is_none()
531 );
532 }
533}
534
535#[derive(Debug, Clone, PartialEq, Eq)]
538pub struct FontError {
539 pub message: String,
540 pub repeated: bool,
541}
542
543impl From<String> for FontError {
544 fn from(message: String) -> FontError {
545 FontError {
546 message,
547 repeated: false,
548 }
549 }
550}
551
552impl From<&str> for FontError {
553 fn from(message: &str) -> FontError {
554 message.to_string().into()
555 }
556}
557
558impl std::fmt::Display for FontError {
559 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560 f.write_str(&self.message)
561 }
562}
563
564#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
566pub struct FontRequest {
567 pub name: String,
568 pub version: String,
569}