use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use uuid::Uuid;
use std::path::PathBuf;
use apimock_routing::view::RouteCatalogSnapshot;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NodeId(pub Uuid);
impl NodeId {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl Default for NodeId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for NodeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct WorkspaceSnapshot {
pub files: Vec<ConfigFileView>,
pub routes: RouteCatalogSnapshot,
pub diagnostics: Vec<Diagnostic>,
}
impl WorkspaceSnapshot {
pub fn empty() -> Self {
Self {
files: Vec::new(),
routes: RouteCatalogSnapshot::empty(),
diagnostics: Vec::new(),
}
}
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct ConfigFileView {
pub path: PathBuf,
pub display_name: String,
pub kind: ConfigFileKind,
pub nodes: Vec<ConfigNodeView>,
}
#[derive(Clone, Copy, Debug, Serialize)]
pub enum ConfigFileKind {
Root,
RuleSet,
Middleware,
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct ConfigNodeView {
pub id: NodeId,
pub source_file: PathBuf,
pub toml_path: String,
pub display_name: String,
pub kind: NodeKind,
pub validation: NodeValidation,
}
#[derive(Clone, Copy, Debug, Serialize)]
pub enum NodeKind {
RootSetting,
RuleSet,
Rule,
Respond,
FileNode,
Script,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct NodeValidation {
pub ok: bool,
pub issues: Vec<ValidationIssue>,
}
impl NodeValidation {
pub fn ok() -> Self {
Self {
ok: true,
issues: Vec::new(),
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct ValidationIssue {
pub severity: Severity,
pub message: String,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum EditCommand {
AddRuleSet {
path: String,
},
RemoveRuleSet {
id: NodeId,
},
AddRule {
parent: NodeId,
rule: RulePayload,
},
UpdateRule {
id: NodeId,
rule: RulePayload,
},
DeleteRule {
id: NodeId,
},
MoveRule {
id: NodeId,
new_index: usize,
},
UpdateRespond {
id: NodeId,
respond: RespondPayload,
},
UpdateRootSetting {
key: RootSettingKey,
value: EditValue,
},
}
#[derive(Clone, Debug, Default)]
pub struct RulePayload {
pub url_path: Option<String>,
pub url_path_op: Option<UrlPathOp>,
pub method: Option<String>,
pub headers: Option<Vec<HeaderConditionPayload>>,
pub body: Option<Vec<BodyConditionPayload>>,
pub respond: RespondPayload,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UrlPathOp {
Equal,
StartsWith,
Contains,
EndsWith,
WildCard,
NotEqual,
}
#[derive(Clone, Debug)]
pub struct HeaderConditionPayload {
pub name: String,
pub op: HeaderOp,
pub value: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HeaderOp {
Equal,
Contains,
StartsWith,
EndsWith,
Regex,
Exists,
Absent,
NotEqual,
WildCard,
}
#[derive(Clone, Debug)]
pub struct BodyConditionPayload {
pub kind: BodyConditionKind,
pub path: String,
pub op: BodyOp,
pub value: serde_json::Value,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BodyConditionKind {
Json,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BodyOp {
Equal,
EqualString,
Contains,
StartsWith,
EndsWith,
Regex,
EqualTyped,
EqualNumber,
GreaterThan,
LessThan,
GreaterOrEqual,
LessOrEqual,
Exists,
Absent,
ArrayLengthEqual,
ArrayLengthAtLeast,
ArrayContains,
EqualInteger,
}
#[derive(Clone, Debug, Default)]
pub struct RespondPayload {
pub file_path: Option<String>,
pub text: Option<String>,
pub status: Option<u16>,
pub delay_milliseconds: Option<u32>,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum RootSettingKey {
ListenerIpAddress,
ListenerPort,
ServiceFallbackRespondDir,
ServiceStrategy,
TlsEnabled,
TlsCertFile,
TlsKeyFile,
LogLevel,
LogFile,
LogFormat,
FileTreeShowHidden,
FileTreeBuiltinExcludes,
FileTreeExtraExcludes,
FileTreeInclude,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum EditValue {
String(String),
Integer(i64),
Boolean(bool),
StringList(Vec<String>),
Enum(String),
Json(JsonValue),
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct ApplyResult {
pub changed_nodes: Vec<NodeId>,
pub diagnostics: Vec<Diagnostic>,
pub requires_reload: bool,
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct SaveResult {
pub changed_files: Vec<PathBuf>,
pub diff_summary: Vec<DiffItem>,
pub requires_reload: bool,
}
#[derive(Clone, Debug, Serialize)]
pub struct DiffItem {
pub kind: DiffKind,
pub target: NodeId,
pub summary: String,
}
#[derive(Clone, Copy, Debug, Serialize)]
pub enum DiffKind {
Added,
Updated,
Removed,
}
#[derive(Clone, Debug, Serialize)]
pub struct ValidationReport {
pub diagnostics: Vec<Diagnostic>,
pub is_valid: bool,
}
impl ValidationReport {
pub fn ok() -> Self {
Self {
diagnostics: Vec::new(),
is_valid: true,
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct Diagnostic {
pub node_id: Option<NodeId>,
pub file: Option<PathBuf>,
pub severity: Severity,
pub message: String,
}
#[derive(Clone, Copy, Debug, Serialize)]
pub enum Severity {
Error,
Warning,
Info,
}
#[derive(Clone, Copy, Debug, Default, Serialize)]
pub struct ReloadHint {
pub requires_reload: bool,
pub requires_restart: bool,
}
impl ReloadHint {
pub fn none() -> Self {
Self::default()
}
pub fn reload() -> Self {
Self {
requires_reload: true,
requires_restart: false,
}
}
pub fn restart() -> Self {
Self {
requires_reload: false,
requires_restart: true,
}
}
pub fn for_key(key: RootSettingKey) -> Self {
use RootSettingKey::*;
match key {
ListenerIpAddress | ListenerPort | TlsEnabled | TlsCertFile | TlsKeyFile
| LogFile => Self::restart(),
ServiceFallbackRespondDir | ServiceStrategy | LogLevel | LogFormat
| FileTreeShowHidden | FileTreeBuiltinExcludes | FileTreeExtraExcludes
| FileTreeInclude => Self::reload(),
}
}
}