use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
pub use crate::options::OptionField;
use crate::{Kind, PluginId};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Envelope<T> {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<u64>,
#[serde(flatten)]
pub body: T,
}
impl<T> Envelope<T> {
pub fn new(id: Option<u64>, body: T) -> Envelope<T> {
Envelope { id, body }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Request {
Hello {
min_version: u32,
max_version: u32,
core_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
plugin: Option<PluginId>,
},
Describe {},
Validate {
options: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
template: Option<Value>,
},
ValidateConnection {
connection: Map<String, Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
unresolved: Vec<String>,
},
Open {
connection: Map<String, Value>,
read_only: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
sandbox: bool,
},
Execute {
sql: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
row_limit: Option<u64>,
},
Check {
sql: String,
},
Write {
path: String,
format: String,
options: Map<String, Value>,
result_sets: Vec<ResultSetMeta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
template: Option<Value>,
},
ResultSetEnd {},
Finish {},
Deliver {
#[serde(default, skip_serializing_if = "Option::is_none")]
local_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
remote_path: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
files: Vec<DeliveryFile>,
connection: Map<String, Value>,
#[serde(default)]
options: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
message: Option<Message>,
},
Load {
name: String,
},
Close {},
Cancel {},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Response {
Hello {
protocol_version: u32,
kind: Kind,
name: String,
version: String,
#[serde(default)]
capabilities: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
provides: Vec<PluginId>,
},
VersionMismatch {
min_version: u32,
max_version: u32,
},
Describe {
connection_fields: Vec<ConnectionField>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
option_fields: Vec<OptionField>,
#[serde(default, skip_serializing_if = "Option::is_none")]
identifier_quote: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
string_literal: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
message_limit: Option<u64>,
},
Validated {
errors: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
warnings: Vec<String>,
},
Ok {},
Result {
columns: Vec<String>,
},
ResultEnd {
rows: u64,
},
NoResult {
#[serde(default, skip_serializing_if = "Option::is_none")]
rows_affected: Option<u64>,
},
Written {
files: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
warnings: Vec<String>,
},
Delivered {
location: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
attempts: Option<u32>,
},
Loaded {
relation: String,
rows: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
warning: Option<String>,
},
Error {
message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
code: Option<String>,
},
Log {
level: LogLevel,
message: String,
#[serde(default, skip_serializing_if = "Map::is_empty")]
fields: Map<String, Value>,
},
Progress {
#[serde(default, skip_serializing_if = "Option::is_none")]
message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
done: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
total: Option<u64>,
},
}
impl Response {
pub fn error(message: impl Into<String>) -> Response {
Response::Error {
message: message.into(),
kind: None,
code: None,
}
}
}
pub const ERROR_KINDS: &[&str] = &[
"config",
"plugin",
"refused",
"connection",
"auth",
"query",
"delivery",
"internal",
"cancelled",
"timed_out",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Error,
Warn,
Info,
Debug,
Trace,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub title: String,
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
pub path: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeliveryFile {
pub local_path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub remote_path: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResultSetMeta {
pub name: String,
pub query: String,
pub result_index: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub anchor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub header: Option<bool>,
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub columns: std::collections::BTreeMap<String, ColumnOptions>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub autofit: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub style: Option<crate::style::SheetStyle>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ColumnOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub formula: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub width: Option<ColumnWidth>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub style: Option<crate::style::CellStyle>,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ColumnWidth {
Chars(f64),
Auto(AutoWidth),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AutoWidth {
Auto,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ConnectionField {
pub name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub required: bool,
#[serde(default)]
pub secret: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub same_as_source: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub manual: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<FieldKind>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub choices: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FieldKind {
String,
Integer,
Boolean,
Path,
Duration,
Map,
}
impl ConnectionField {
pub fn new(name: &str, description: &str) -> Self {
ConnectionField {
name: name.into(),
description: description.into(),
required: false,
secret: false,
default: None,
same_as_source: None,
manual: false,
kind: None,
choices: Vec::new(),
}
}
pub fn kind(mut self, kind: FieldKind) -> Self {
self.kind = Some(kind);
self
}
pub fn choices(mut self, choices: &[&str]) -> Self {
self.choices = choices.iter().map(|c| c.to_string()).collect();
self.kind.get_or_insert(FieldKind::String);
self
}
pub fn required(mut self) -> Self {
self.required = true;
self
}
pub fn secret(mut self) -> Self {
self.secret = true;
self
}
pub fn default(mut self, v: impl Into<Value>) -> Self {
self.default = Some(v.into());
self
}
pub fn same_as_source(mut self, source_type: &str) -> Self {
self.same_as_source = Some(source_type.into());
self
}
pub fn manual(mut self) -> Self {
self.manual = true;
self
}
}