use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Run {
pub id: String,
pub name: String,
pub timestamp: DateTime<Utc>,
pub command: String,
pub vault: String,
pub project_root: String,
pub exit_code: Option<i32>,
pub duration_ms: Option<i32>,
pub stdout: Option<String>,
pub stderr: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateRunRequest {
pub id: String,
pub name: String,
pub timestamp: DateTime<Utc>,
pub command: String,
pub vault: String,
pub project_root: String,
pub exit_code: Option<i32>,
pub duration_ms: Option<i32>,
pub stdout: Option<String>,
pub stderr: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct ListRunsQuery {
pub vault: Option<String>,
pub limit: Option<i64>,
pub offset: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HookOutput {
#[serde(rename = "__meta")]
pub meta: HookMeta,
#[serde(flatten)]
pub output: JsonValue,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HookMeta {
pub id: String,
pub config: Option<JsonValue>,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct RunOutputRow {
pub phase: String,
pub hook_id: String,
pub config: Option<JsonValue>,
pub output: JsonValue,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct CapturedFile {
pub path: String,
pub size: i64,
pub hash: Option<String>,
pub storage_path: String,
pub content_type: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct SearchRunsRequest {
pub vault: Option<String>,
pub from: Option<DateTime<Utc>>,
pub to: Option<DateTime<Utc>>,
pub exit_code: Option<i32>,
pub success: Option<bool>,
#[serde(default)]
pub hook_filters: Vec<HookFilter>,
#[serde(default)]
pub include: Vec<IncludeField>,
#[serde(default)]
pub order: SortOrder,
pub limit: Option<i64>,
pub offset: Option<i64>,
}
#[derive(Debug, Deserialize)]
pub struct HookFilter {
pub hook_id: String,
pub config_filter: Option<String>,
pub output_filter: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum IncludeField {
Metadata,
Files,
Stdout,
Stderr,
Hooks,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SortOrder {
#[default]
LatestFirst,
OldestFirst,
}
#[derive(Debug, Serialize)]
pub struct SearchRunResult {
pub id: String,
pub name: String,
pub timestamp: DateTime<Utc>,
pub vault: String,
pub command: String,
pub project_root: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub exit_code: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stdout: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stderr: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<FileInfo>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pre_run_hooks: Option<Vec<HookOutput>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_run_hooks: Option<Vec<HookOutput>>,
}
#[derive(Debug, Clone, Serialize)]
pub struct FileInfo {
pub path: String,
pub size: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub hash: Option<String>,
pub url: String,
}
#[derive(Debug, Serialize)]
pub struct SearchRunsResponse {
pub status: String,
pub total: i64,
pub runs: Vec<SearchRunResult>,
}