use crate::error::Result; use crate::models::HttpRequestIden::{ Authentication, AuthenticationType, Body, BodyType, CreatedAt, Description, FolderId, Headers, Method, Name, SortPriority, UpdatedAt, Url, UrlParameters, WorkspaceId, }; use crate::queries_legacy::UpdateSource; use chrono::{NaiveDateTime, Utc}; use rusqlite::Row; use sea_query::{enum_def, IntoIden, IntoTableRef, SimpleExpr}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use std::collections::BTreeMap; use std::fmt::Display; use std::str::FromStr; use ts_rs::TS; #[macro_export] macro_rules! impl_model { ($t:ty, $variant:ident) => { impl $crate::Model for $t { fn into_any(self) -> $crate::AnyModel { $crate::AnyModel::$variant(self) } } }; } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase", tag = "type")] #[ts(export, export_to = "gen_models.ts")] pub enum ProxySetting { Enabled { http: String, https: String, auth: Option, }, Disabled, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct ProxySettingAuth { pub user: String, pub password: String, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum EditorKeymap { Default, Vim, Vscode, Emacs, } impl FromStr for EditorKeymap { type Err = crate::error::Error; fn from_str(s: &str) -> Result { match s { "default" => Ok(Self::Default), "vscode" => Ok(Self::Vscode), "vim" => Ok(Self::Vim), "emacs" => Ok(Self::Emacs), _ => Ok(Self::default()), } } } impl Display for EditorKeymap { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { EditorKeymap::Default => "default".to_string(), EditorKeymap::Vscode => "vscode".to_string(), EditorKeymap::Vim => "vim".to_string(), EditorKeymap::Emacs => "emacs".to_string(), }; write!(f, "{}", str) } } impl Default for EditorKeymap { fn default() -> Self { Self::Default } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "settings")] pub struct Settings { #[ts(type = "\"settings\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub appearance: String, pub editor_font_size: i32, pub editor_soft_wrap: bool, pub interface_font_size: i32, pub interface_scale: f32, pub open_workspace_new_window: Option, pub proxy: Option, pub theme: String, pub theme_dark: String, pub theme_light: String, pub update_channel: String, pub editor_keymap: EditorKeymap, } impl UpsertModelInfo for Settings { fn table_name() -> impl IntoTableRef { SettingsIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { SettingsIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use SettingsIden::*; let proxy = match self.proxy { None => None, Some(p) => Some(serde_json::to_string(&p)?), }; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (Appearance, self.appearance.as_str().into()), (EditorFontSize, self.editor_font_size.into()), (EditorKeymap, self.editor_keymap.to_string().into()), (EditorSoftWrap, self.editor_soft_wrap.into()), (InterfaceFontSize, self.interface_font_size.into()), (InterfaceScale, self.interface_scale.into()), (OpenWorkspaceNewWindow, self.open_workspace_new_window.into()), (Theme, self.theme.as_str().into()), (ThemeDark, self.theme_dark.as_str().into()), (ThemeLight, self.theme_light.as_str().into()), (UpdateChannel, self.update_channel.into()), (Proxy, proxy.into()), ]) } fn update_columns() -> Vec { vec![ SettingsIden::UpdatedAt, SettingsIden::Appearance, SettingsIden::EditorFontSize, SettingsIden::EditorKeymap, SettingsIden::EditorSoftWrap, SettingsIden::InterfaceFontSize, SettingsIden::InterfaceScale, SettingsIden::OpenWorkspaceNewWindow, SettingsIden::Proxy, SettingsIden::Theme, SettingsIden::ThemeDark, SettingsIden::ThemeLight, SettingsIden::UpdateChannel, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let proxy: Option = row.get("proxy")?; let editor_keymap: String = row.get("editor_keymap")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, appearance: row.get("appearance")?, editor_font_size: row.get("editor_font_size")?, editor_keymap: EditorKeymap::from_str(editor_keymap.as_str()).unwrap(), editor_soft_wrap: row.get("editor_soft_wrap")?, interface_font_size: row.get("interface_font_size")?, interface_scale: row.get("interface_scale")?, open_workspace_new_window: row.get("open_workspace_new_window")?, proxy: proxy.map(|p| -> ProxySetting { serde_json::from_str(p.as_str()).unwrap() }), theme: row.get("theme")?, theme_dark: row.get("theme_dark")?, theme_light: row.get("theme_light")?, update_channel: row.get("update_channel")?, }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "workspaces")] pub struct Workspace { #[ts(type = "\"workspace\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub name: String, pub description: String, // Settings #[serde(default = "default_true")] pub setting_validate_certificates: bool, #[serde(default = "default_true")] pub setting_follow_redirects: bool, pub setting_request_timeout: i32, } impl UpsertModelInfo for Workspace { fn table_name() -> impl IntoTableRef { WorkspaceIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { WorkspaceIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use WorkspaceIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (Name, self.name.trim().into()), (Description, self.description.into()), (SettingFollowRedirects, self.setting_follow_redirects.into()), (SettingRequestTimeout, self.setting_request_timeout.into()), (SettingValidateCertificates, self.setting_validate_certificates.into()), ]) } fn update_columns() -> Vec { vec![ WorkspaceIden::UpdatedAt, WorkspaceIden::Name, WorkspaceIden::Description, WorkspaceIden::SettingRequestTimeout, WorkspaceIden::SettingFollowRedirects, WorkspaceIden::SettingRequestTimeout, WorkspaceIden::SettingValidateCertificates, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { Ok(Self { id: row.get("id")?, model: row.get("model")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, name: row.get("name")?, description: row.get("description")?, setting_follow_redirects: row.get("setting_follow_redirects")?, setting_request_timeout: row.get("setting_request_timeout")?, setting_validate_certificates: row.get("setting_validate_certificates")?, }) } } impl Workspace { pub fn new(name: String) -> Self { Self { name, model: "workspace".to_string(), setting_validate_certificates: true, setting_follow_redirects: true, ..Default::default() } } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "workspace_metas")] pub struct WorkspaceMeta { #[ts(type = "\"workspace_meta\"")] pub model: String, pub id: String, pub workspace_id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub setting_sync_dir: Option, } impl UpsertModelInfo for WorkspaceMeta { fn table_name() -> impl IntoTableRef { WorkspaceMetaIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { WorkspaceMetaIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use WorkspaceMetaIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (SettingSyncDir, self.setting_sync_dir.into()), ]) } fn update_columns() -> Vec { vec![ WorkspaceMetaIden::UpdatedAt, WorkspaceMetaIden::SettingSyncDir, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { Ok(Self { id: row.get("id")?, workspace_id: row.get("workspace_id")?, model: row.get("model")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, setting_sync_dir: row.get("setting_sync_dir")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export, export_to = "gen_models.ts")] enum CookieDomain { HostOnly(String), Suffix(String), NotPresent, Empty, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export, export_to = "gen_models.ts")] enum CookieExpires { AtUtc(String), SessionEnd, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export, export_to = "gen_models.ts")] pub struct Cookie { raw_cookie: String, domain: CookieDomain, expires: CookieExpires, path: (String, bool), } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "cookie_jars")] pub struct CookieJar { #[ts(type = "\"cookie_jar\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub cookies: Vec, pub name: String, } impl UpsertModelInfo for CookieJar { fn table_name() -> impl IntoTableRef { CookieJarIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { CookieJarIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use CookieJarIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (Name, self.name.trim().into()), (Cookies, serde_json::to_string(&self.cookies)?.into()), ]) } fn update_columns() -> Vec { vec![ CookieJarIden::UpdatedAt, CookieJarIden::Name, CookieJarIden::Cookies, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let cookies: String = row.get("cookies")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, name: row.get("name")?, cookies: serde_json::from_str(cookies.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "environments")] pub struct Environment { #[ts(type = "\"environment\"")] pub model: String, pub id: String, pub workspace_id: String, pub environment_id: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub name: String, pub variables: Vec, } impl UpsertModelInfo for Environment { fn table_name() -> impl IntoTableRef { EnvironmentIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { EnvironmentIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use EnvironmentIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (EnvironmentId, self.environment_id.into()), (WorkspaceId, self.workspace_id.into()), (Name, self.name.trim().into()), (Variables, serde_json::to_string(&self.variables)?.into()), ]) } fn update_columns() -> Vec { vec![ EnvironmentIden::UpdatedAt, EnvironmentIden::Name, EnvironmentIden::Variables, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let variables: String = row.get("variables")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, environment_id: row.get("environment_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, name: row.get("name")?, variables: serde_json::from_str(variables.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct EnvironmentVariable { #[serde(default = "default_true")] #[ts(optional, as = "Option")] pub enabled: bool, pub name: String, pub value: String, #[ts(optional, as = "Option")] pub id: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "folders")] pub struct Folder { #[ts(type = "\"folder\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub folder_id: Option, pub name: String, pub description: String, pub sort_priority: f32, } impl UpsertModelInfo for Folder { fn table_name() -> impl IntoTableRef { FolderIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { FolderIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use FolderIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (FolderId, self.folder_id.into()), (Name, self.name.trim().into()), (Description, self.description.into()), (SortPriority, self.sort_priority.into()), ]) } fn update_columns() -> Vec { vec![ FolderIden::UpdatedAt, FolderIden::Name, FolderIden::Description, FolderIden::FolderId, FolderIden::SortPriority, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { Ok(Self { id: row.get("id")?, model: row.get("model")?, sort_priority: row.get("sort_priority")?, workspace_id: row.get("workspace_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, folder_id: row.get("folder_id")?, name: row.get("name")?, description: row.get("description")?, }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct HttpRequestHeader { #[serde(default = "default_true")] #[ts(optional, as = "Option")] pub enabled: bool, pub name: String, pub value: String, #[ts(optional, as = "Option")] pub id: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct HttpUrlParameter { #[serde(default = "default_true")] #[ts(optional, as = "Option")] pub enabled: bool, pub name: String, pub value: String, #[ts(optional, as = "Option")] pub id: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "http_requests")] pub struct HttpRequest { #[ts(type = "\"http_request\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub folder_id: Option, #[ts(type = "Record")] pub authentication: BTreeMap, pub authentication_type: Option, #[ts(type = "Record")] pub body: BTreeMap, pub body_type: Option, pub description: String, pub headers: Vec, #[serde(default = "default_http_method")] pub method: String, pub name: String, pub sort_priority: f32, pub url: String, pub url_parameters: Vec, } impl UpsertModelInfo for HttpRequest { fn table_name() -> impl IntoTableRef { HttpRequestIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { HttpRequestIden::Id } fn get_id(&self) -> String { self.id.to_string() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (FolderId, self.folder_id.into()), (Name, self.name.trim().into()), (Description, self.description.into()), (Url, self.url.into()), (UrlParameters, serde_json::to_string(&self.url_parameters)?.into()), (Method, self.method.into()), (Body, serde_json::to_string(&self.body)?.into()), (BodyType, self.body_type.into()), (Authentication, serde_json::to_string(&self.authentication)?.into()), (AuthenticationType, self.authentication_type.into()), (Headers, serde_json::to_string(&self.headers)?.into()), (SortPriority, self.sort_priority.into()), ]) } fn update_columns() -> Vec { vec![ UpdatedAt, WorkspaceId, Name, Description, FolderId, Method, Headers, Body, BodyType, Authentication, AuthenticationType, Url, UrlParameters, SortPriority, ] } fn from_row(r: &Row) -> rusqlite::Result { let url_parameters: String = r.get("url_parameters")?; let body: String = r.get("body")?; let authentication: String = r.get("authentication")?; let headers: String = r.get("headers")?; Ok(Self { id: r.get("id")?, model: r.get("model")?, sort_priority: r.get("sort_priority")?, workspace_id: r.get("workspace_id")?, created_at: r.get("created_at")?, updated_at: r.get("updated_at")?, url: r.get("url")?, url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(), method: r.get("method")?, body: serde_json::from_str(body.as_str()).unwrap_or_default(), body_type: r.get("body_type")?, description: r.get("description")?, authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(), authentication_type: r.get("authentication_type")?, headers: serde_json::from_str(headers.as_str()).unwrap_or_default(), folder_id: r.get("folder_id")?, name: r.get("name")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum WebsocketConnectionState { Initialized, Connected, Closing, Closed, } impl Default for WebsocketConnectionState { fn default() -> Self { Self::Initialized } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "websocket_connections")] pub struct WebsocketConnection { #[ts(type = "\"websocket_connection\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub request_id: String, pub elapsed: i32, pub error: Option, pub headers: Vec, pub state: WebsocketConnectionState, pub status: i32, pub url: String, } impl UpsertModelInfo for WebsocketConnection { fn table_name() -> impl IntoTableRef { WebsocketConnectionIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { WebsocketConnectionIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use WebsocketConnectionIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (RequestId, self.request_id.into()), (Elapsed, self.elapsed.into()), (Error, self.error.into()), (Headers, serde_json::to_string(&self.headers)?.into()), (State, serde_json::to_value(&self.state)?.as_str().into()), (Status, self.status.into()), (Url, self.url.into()), ]) } fn update_columns() -> Vec { vec![ WebsocketConnectionIden::UpdatedAt, WebsocketConnectionIden::Elapsed, WebsocketConnectionIden::Error, WebsocketConnectionIden::Headers, WebsocketConnectionIden::State, WebsocketConnectionIden::Status, WebsocketConnectionIden::Url, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let headers: String = row.get("headers")?; let state: String = row.get("state")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, request_id: row.get("request_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, url: row.get("url")?, headers: serde_json::from_str(headers.as_str()).unwrap_or_default(), elapsed: row.get("elapsed")?, error: row.get("error")?, state: serde_json::from_str(format!(r#""{state}""#).as_str()).unwrap(), status: row.get("status")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum WebsocketMessageType { Text, Binary, } impl Default for WebsocketMessageType { fn default() -> Self { Self::Text } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "websocket_requests")] pub struct WebsocketRequest { #[ts(type = "\"websocket_request\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub folder_id: Option, #[ts(type = "Record")] pub authentication: BTreeMap, pub authentication_type: Option, pub description: String, pub headers: Vec, pub message: String, pub name: String, pub sort_priority: f32, pub url: String, pub url_parameters: Vec, } impl UpsertModelInfo for WebsocketRequest { fn table_name() -> impl IntoTableRef { WebsocketRequestIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { WebsocketRequestIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use WebsocketRequestIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (FolderId, self.folder_id.as_ref().map(|s| s.as_str()).into()), (Authentication, serde_json::to_string(&self.authentication)?.into()), (AuthenticationType, self.authentication_type.as_ref().map(|s| s.as_str()).into()), (Description, self.description.into()), (Headers, serde_json::to_string(&self.headers)?.into()), (Message, self.message.into()), (Name, self.name.trim().into()), (SortPriority, self.sort_priority.into()), (Url, self.url.into()), (UrlParameters, serde_json::to_string(&self.url_parameters)?.into()), ]) } fn update_columns() -> Vec { vec![ WebsocketRequestIden::UpdatedAt, WebsocketRequestIden::WorkspaceId, WebsocketRequestIden::FolderId, WebsocketRequestIden::Authentication, WebsocketRequestIden::AuthenticationType, WebsocketRequestIden::Description, WebsocketRequestIden::Headers, WebsocketRequestIden::Message, WebsocketRequestIden::Name, WebsocketRequestIden::SortPriority, WebsocketRequestIden::Url, WebsocketRequestIden::UrlParameters, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let url_parameters: String = row.get("url_parameters")?; let authentication: String = row.get("authentication")?; let headers: String = row.get("headers")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, sort_priority: row.get("sort_priority")?, workspace_id: row.get("workspace_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, url: row.get("url")?, url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(), message: row.get("message")?, description: row.get("description")?, authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(), authentication_type: row.get("authentication_type")?, headers: serde_json::from_str(headers.as_str()).unwrap_or_default(), folder_id: row.get("folder_id")?, name: row.get("name")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum WebsocketEventType { Binary, Close, Frame, Open, Ping, Pong, Text, } impl Default for WebsocketEventType { fn default() -> Self { Self::Text } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "websocket_events")] pub struct WebsocketEvent { #[ts(type = "\"websocket_event\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub request_id: String, pub connection_id: String, pub is_server: bool, pub message: Vec, pub message_type: WebsocketEventType, } impl UpsertModelInfo for WebsocketEvent { fn table_name() -> impl IntoTableRef { WebsocketEventIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { WebsocketEventIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use WebsocketEventIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (ConnectionId, self.connection_id.into()), (RequestId, self.request_id.into()), (MessageType, serde_json::to_string(&self.message_type)?.into()), (IsServer, self.is_server.into()), (Message, self.message.into()), ]) } fn update_columns() -> Vec { vec![ WebsocketEventIden::UpdatedAt, WebsocketEventIden::MessageType, WebsocketEventIden::IsServer, WebsocketEventIden::Message, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let message_type: String = row.get("message_type")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, request_id: row.get("request_id")?, connection_id: row.get("connection_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, message: row.get("message")?, is_server: row.get("is_server")?, message_type: serde_json::from_str(message_type.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct HttpResponseHeader { pub name: String, pub value: String, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum HttpResponseState { Initialized, Connected, Closed, } impl Default for HttpResponseState { fn default() -> Self { Self::Initialized } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "http_responses")] pub struct HttpResponse { #[ts(type = "\"http_response\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub request_id: String, pub body_path: Option, pub content_length: Option, pub elapsed: i32, pub elapsed_headers: i32, pub error: Option, pub headers: Vec, pub remote_addr: Option, pub status: i32, pub status_reason: Option, pub state: HttpResponseState, pub url: String, pub version: Option, } impl UpsertModelInfo for HttpResponse { fn table_name() -> impl IntoTableRef { HttpResponseIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { HttpResponseIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use HttpResponseIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (RequestId, self.request_id.into()), (WorkspaceId, self.workspace_id.into()), (BodyPath, self.body_path.into()), (ContentLength, self.content_length.into()), (Elapsed, self.elapsed.into()), (ElapsedHeaders, self.elapsed_headers.into()), (Error, self.error.into()), (Headers, serde_json::to_string(&self.headers)?.into()), (RemoteAddr, self.remote_addr.into()), (State, serde_json::to_value(self.state)?.as_str().into()), (Status, self.status.into()), (StatusReason, self.status_reason.into()), (Url, self.url.into()), (Version, self.version.into()), ]) } fn update_columns() -> Vec { vec![ HttpResponseIden::UpdatedAt, HttpResponseIden::BodyPath, HttpResponseIden::ContentLength, HttpResponseIden::Elapsed, HttpResponseIden::ElapsedHeaders, HttpResponseIden::Error, HttpResponseIden::Headers, HttpResponseIden::RemoteAddr, HttpResponseIden::State, HttpResponseIden::Status, HttpResponseIden::StatusReason, HttpResponseIden::Url, HttpResponseIden::Version, ] } fn from_row(r: &Row) -> rusqlite::Result where Self: Sized, { let headers: String = r.get("headers")?; let state: String = r.get("state")?; Ok(Self { id: r.get("id")?, model: r.get("model")?, workspace_id: r.get("workspace_id")?, request_id: r.get("request_id")?, created_at: r.get("created_at")?, updated_at: r.get("updated_at")?, error: r.get("error")?, url: r.get("url")?, content_length: r.get("content_length")?, version: r.get("version")?, elapsed: r.get("elapsed")?, elapsed_headers: r.get("elapsed_headers")?, remote_addr: r.get("remote_addr")?, status: r.get("status")?, status_reason: r.get("status_reason")?, state: serde_json::from_str(format!(r#""{state}""#).as_str()).unwrap(), body_path: r.get("body_path")?, headers: serde_json::from_str(headers.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] pub struct GrpcMetadataEntry { #[serde(default = "default_true")] #[ts(optional, as = "Option")] pub enabled: bool, pub name: String, pub value: String, #[ts(optional, as = "Option")] pub id: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "grpc_requests")] pub struct GrpcRequest { #[ts(type = "\"grpc_request\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub folder_id: Option, pub authentication_type: Option, #[ts(type = "Record")] pub authentication: BTreeMap, pub description: String, pub message: String, pub metadata: Vec, pub method: Option, pub name: String, pub service: Option, pub sort_priority: f32, pub url: String, } impl UpsertModelInfo for GrpcRequest { fn table_name() -> impl IntoTableRef { GrpcRequestIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { GrpcRequestIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use GrpcRequestIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (Name, self.name.trim().into()), (Description, self.description.into()), (WorkspaceId, self.workspace_id.into()), (FolderId, self.folder_id.into()), (SortPriority, self.sort_priority.into()), (Url, self.url.into()), (Service, self.service.into()), (Method, self.method.into()), (Message, self.message.into()), (AuthenticationType, self.authentication_type.into()), (Authentication, serde_json::to_string(&self.authentication)?.into()), (Metadata, serde_json::to_string(&self.metadata)?.into()), ]) } fn update_columns() -> Vec { vec![ GrpcRequestIden::UpdatedAt, GrpcRequestIden::WorkspaceId, GrpcRequestIden::Name, GrpcRequestIden::Description, GrpcRequestIden::FolderId, GrpcRequestIden::SortPriority, GrpcRequestIden::Url, GrpcRequestIden::Service, GrpcRequestIden::Method, GrpcRequestIden::Message, GrpcRequestIden::AuthenticationType, GrpcRequestIden::Authentication, GrpcRequestIden::Metadata, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let authentication: String = row.get("authentication")?; let metadata: String = row.get("metadata")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, folder_id: row.get("folder_id")?, name: row.get("name")?, description: row.get("description")?, service: row.get("service")?, method: row.get("method")?, message: row.get("message")?, authentication_type: row.get("authentication_type")?, authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(), url: row.get("url")?, sort_priority: row.get("sort_priority")?, metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum GrpcConnectionState { Initialized, Connected, Closed, } impl Default for GrpcConnectionState { fn default() -> Self { Self::Initialized } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "grpc_connections")] pub struct GrpcConnection { #[ts(type = "\"grpc_connection\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub request_id: String, pub elapsed: i32, pub error: Option, pub method: String, pub service: String, pub status: i32, pub state: GrpcConnectionState, pub trailers: BTreeMap, pub url: String, } impl UpsertModelInfo for GrpcConnection { fn table_name() -> impl IntoTableRef { GrpcConnectionIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { GrpcConnectionIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use GrpcConnectionIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (RequestId, self.request_id.into()), (Service, self.service.into()), (Method, self.method.into()), (Elapsed, self.elapsed.into()), (State, serde_json::to_value(&self.state)?.as_str().into()), (Status, self.status.into()), (Error, self.error.as_ref().map(|s| s.as_str()).into()), (Trailers, serde_json::to_string(&self.trailers)?.into()), (Url, self.url.into()), ]) } fn update_columns() -> Vec { vec![ GrpcConnectionIden::UpdatedAt, GrpcConnectionIden::Service, GrpcConnectionIden::Method, GrpcConnectionIden::Elapsed, GrpcConnectionIden::Status, GrpcConnectionIden::State, GrpcConnectionIden::Error, GrpcConnectionIden::Trailers, GrpcConnectionIden::Url, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let trailers: String = row.get("trailers")?; let state: String = row.get("state")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, request_id: row.get("request_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, service: row.get("service")?, method: row.get("method")?, elapsed: row.get("elapsed")?, state: serde_json::from_str(format!(r#""{state}""#).as_str()).unwrap(), status: row.get("status")?, url: row.get("url")?, error: row.get("error")?, trailers: serde_json::from_str(trailers.as_str()).unwrap_or_default(), }) } } #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)] #[serde(rename_all = "snake_case")] #[ts(export, export_to = "gen_models.ts")] pub enum GrpcEventType { Info, Error, ClientMessage, ServerMessage, ConnectionStart, ConnectionEnd, } impl Default for GrpcEventType { fn default() -> Self { GrpcEventType::Info } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "grpc_events")] pub struct GrpcEvent { #[ts(type = "\"grpc_event\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub workspace_id: String, pub request_id: String, pub connection_id: String, pub content: String, pub error: Option, pub event_type: GrpcEventType, pub metadata: BTreeMap, pub status: Option, } impl UpsertModelInfo for GrpcEvent { fn table_name() -> impl IntoTableRef { GrpcEventIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { GrpcEventIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use GrpcEventIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (RequestId, self.request_id.into()), (ConnectionId, self.connection_id.into()), (Content, self.content.into()), (EventType, serde_json::to_string(&self.event_type)?.into()), (Metadata, serde_json::to_string(&self.metadata)?.into()), (Status, self.status.into()), (Error, self.error.into()), ]) } fn update_columns() -> Vec { vec![ GrpcEventIden::UpdatedAt, GrpcEventIden::Content, GrpcEventIden::EventType, GrpcEventIden::Metadata, GrpcEventIden::Status, GrpcEventIden::Error, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { let event_type: String = row.get("event_type")?; let metadata: String = row.get("metadata")?; Ok(Self { id: row.get("id")?, model: row.get("model")?, workspace_id: row.get("workspace_id")?, request_id: row.get("request_id")?, connection_id: row.get("connection_id")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, content: row.get("content")?, event_type: serde_json::from_str(event_type.as_str()).unwrap_or_default(), metadata: serde_json::from_str(metadata.as_str()).unwrap_or_default(), status: row.get("status")?, error: row.get("error")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "plugins")] pub struct Plugin { #[ts(type = "\"plugin\"")] pub model: String, pub id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub checked_at: Option, pub directory: String, pub enabled: bool, pub url: Option, } impl UpsertModelInfo for Plugin { fn table_name() -> impl IntoTableRef { PluginIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { PluginIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use PluginIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (CheckedAt, self.checked_at.into()), (Directory, self.directory.into()), (Url, self.url.into()), (Enabled, self.enabled.into()), ]) } fn update_columns() -> Vec { vec![ PluginIden::UpdatedAt, PluginIden::CheckedAt, PluginIden::Directory, PluginIden::Url, PluginIden::Enabled, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { Ok(Self { id: row.get("id")?, model: row.get("model")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, checked_at: row.get("checked_at")?, url: row.get("url")?, directory: row.get("directory")?, enabled: row.get("enabled")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "sync_states")] pub struct SyncState { #[ts(type = "\"sync_state\"")] pub model: String, pub id: String, pub workspace_id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub flushed_at: NaiveDateTime, pub model_id: String, pub checksum: String, pub rel_path: String, pub sync_dir: String, } impl UpsertModelInfo for SyncState { fn table_name() -> impl IntoTableRef { SyncStateIden::Table } fn id_column() -> impl IntoIden + Eq + Clone { SyncStateIden::Id } fn get_id(&self) -> String { self.id.clone() } fn insert_values( self, source: &UpdateSource, ) -> Result)>> { use SyncStateIden::*; Ok(vec![ (CreatedAt, upsert_date(source, self.created_at)), (UpdatedAt, upsert_date(source, self.updated_at)), (WorkspaceId, self.workspace_id.into()), (FlushedAt, self.flushed_at.into()), (Checksum, self.checksum.into()), (ModelId, self.model_id.into()), (RelPath, self.rel_path.into()), (SyncDir, self.sync_dir.into()), ]) } fn update_columns() -> Vec { vec![ SyncStateIden::UpdatedAt, SyncStateIden::FlushedAt, SyncStateIden::Checksum, SyncStateIden::RelPath, SyncStateIden::SyncDir, ] } fn from_row(row: &Row) -> rusqlite::Result where Self: Sized, { Ok(Self { id: row.get("id")?, workspace_id: row.get("workspace_id")?, model: row.get("model")?, created_at: row.get("created_at")?, updated_at: row.get("updated_at")?, flushed_at: row.get("flushed_at")?, checksum: row.get("checksum")?, model_id: row.get("model_id")?, sync_dir: row.get("sync_dir")?, rel_path: row.get("rel_path")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "key_values")] pub struct KeyValue { #[ts(type = "\"key_value\"")] pub model: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub key: String, pub namespace: String, pub value: String, } impl<'s> TryFrom<&Row<'s>> for KeyValue { type Error = rusqlite::Error; fn try_from(r: &Row<'s>) -> std::result::Result { Ok(Self { model: r.get("model")?, created_at: r.get("created_at")?, updated_at: r.get("updated_at")?, namespace: r.get("namespace")?, key: r.get("key")?, value: r.get("value")?, }) } } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "gen_models.ts")] #[enum_def(table_name = "plugin_key_values")] pub struct PluginKeyValue { #[ts(type = "\"plugin_key_value\"")] pub model: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub plugin_name: String, pub key: String, pub value: String, } impl<'s> TryFrom<&Row<'s>> for PluginKeyValue { type Error = rusqlite::Error; fn try_from(r: &Row<'s>) -> std::result::Result { Ok(Self { model: r.get("model")?, created_at: r.get("created_at")?, updated_at: r.get("updated_at")?, plugin_name: r.get("plugin_name")?, key: r.get("key")?, value: r.get("value")?, }) } } fn default_true() -> bool { true } fn default_http_method() -> String { "GET".to_string() } pub enum ModelType { TypeCookieJar, TypeEnvironment, TypeFolder, TypeGrpcConnection, TypeGrpcEvent, TypeGrpcRequest, TypeHttpRequest, TypeHttpResponse, TypePlugin, TypeSyncState, TypeWebSocketConnection, TypeWebSocketEvent, TypeWebsocketRequest, TypeWorkspace, TypeWorkspaceMeta, } impl ModelType { pub fn id_prefix(&self) -> String { match self { ModelType::TypeCookieJar => "cj", ModelType::TypeEnvironment => "ev", ModelType::TypeFolder => "fl", ModelType::TypeGrpcConnection => "gc", ModelType::TypeGrpcEvent => "ge", ModelType::TypeGrpcRequest => "gr", ModelType::TypeHttpRequest => "rq", ModelType::TypeHttpResponse => "rs", ModelType::TypePlugin => "pg", ModelType::TypeWorkspace => "wk", ModelType::TypeWorkspaceMeta => "wm", ModelType::TypeSyncState => "ss", ModelType::TypeWebSocketConnection => "wc", ModelType::TypeWebSocketEvent => "we", ModelType::TypeWebsocketRequest => "wr", } .to_string() } } #[macro_export] macro_rules! define_any_model { ($($type:ident),* $(,)?) => { #[derive(Debug, Clone, Serialize, TS)] #[serde(rename_all = "camelCase", untagged)] #[ts(export, export_to = "gen_models.ts")] pub enum AnyModel { $( $type($type), )* } $( impl From<$type> for AnyModel { fn from(value: $type) -> Self { AnyModel::$type(value) } } impl From for $type { fn from(value: AnyModel) -> $type { match value { AnyModel::$type(inner) => inner, _ => panic!( // Should never happen because this macro also generates the enum variant "Tried to convert AnyModel into `{}`, but found a different variant", stringify!($type) ), } } } )* }; } define_any_model! { CookieJar, Environment, Folder, GrpcConnection, GrpcEvent, GrpcRequest, HttpRequest, HttpResponse, KeyValue, Plugin, Settings, SyncState, WebsocketConnection, WebsocketEvent, WebsocketRequest, Workspace, WorkspaceMeta, } impl<'de> Deserialize<'de> for AnyModel { fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, { let value = Value::deserialize(deserializer)?; let model = value.as_object().unwrap(); let model = match model.get("model") { Some(m) if m == "http_request" => { AnyModel::HttpRequest(serde_json::from_value(value).unwrap()) } Some(m) if m == "grpc_request" => { AnyModel::GrpcRequest(serde_json::from_value(value).unwrap()) } Some(m) if m == "workspace" => { AnyModel::Workspace(serde_json::from_value(value).unwrap()) } Some(m) if m == "environment" => { AnyModel::Environment(serde_json::from_value(value).unwrap()) } Some(m) if m == "folder" => AnyModel::Folder(serde_json::from_value(value).unwrap()), Some(m) if m == "key_value" => { AnyModel::KeyValue(serde_json::from_value(value).unwrap()) } Some(m) if m == "grpc_connection" => { AnyModel::GrpcConnection(serde_json::from_value(value).unwrap()) } Some(m) if m == "grpc_event" => { AnyModel::GrpcEvent(serde_json::from_value(value).unwrap()) } Some(m) if m == "cookie_jar" => { AnyModel::CookieJar(serde_json::from_value(value).unwrap()) } Some(m) if m == "plugin" => AnyModel::Plugin(serde_json::from_value(value).unwrap()), Some(m) => { return Err(serde::de::Error::custom(format!("Unknown model {}", m))); } None => { return Err(serde::de::Error::custom("Missing or invalid model")); } }; Ok(model) } } impl AnyModel { pub fn resolved_name(&self) -> String { let compute_name = |name: &str, url: &str, fallback: &str| -> String { if !name.is_empty() { return name.to_string(); } let without_variables = url.replace(r"\$\{\[\s*([^\]\s]+)\s*]}", "$1"); if without_variables.is_empty() { fallback.to_string() } else { without_variables } }; match self.clone() { AnyModel::CookieJar(v) => v.name, AnyModel::Environment(v) => v.name, AnyModel::Folder(v) => v.name, AnyModel::GrpcRequest(v) => compute_name(&v.name, &v.url, "gRPC Request"), AnyModel::HttpRequest(v) => compute_name(&v.name, &v.url, "HTTP Request"), AnyModel::WebsocketRequest(v) => compute_name(&v.name, &v.url, "WebSocket Request"), AnyModel::Workspace(v) => v.name, _ => "No Name".to_string(), } } } pub trait UpsertModelInfo { fn table_name() -> impl IntoTableRef; fn id_column() -> impl IntoIden + Eq + Clone; fn get_id(&self) -> String; fn insert_values( self, source: &UpdateSource, ) -> Result)>>; fn update_columns() -> Vec; fn from_row(row: &Row) -> rusqlite::Result where Self: Sized; } // Generate the created_at or updated_at timestamps for an upsert operation, depending on the ID // provided. fn upsert_date(update_source: &UpdateSource, dt: NaiveDateTime) -> SimpleExpr { match update_source { // Sync and import operations always preserve timestamps UpdateSource::Sync | UpdateSource::Import => { if dt.and_utc().timestamp() == 0 { // Sometimes data won't have timestamps (partial data) Utc::now().naive_utc().into() } else { dt.into() } } // Other sources will always update to the latest time _ => Utc::now().naive_utc().into(), } }