use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum AppStatus {
Up,
Down,
#[default]
Unknown,
}
impl fmt::Display for AppStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppStatus::Up => write!(f, "UP"),
AppStatus::Down => write!(f, "DOWN"),
AppStatus::Unknown => write!(f, "UNKNOWN"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpringApp {
pub name: String,
pub url: String,
pub status: AppStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Endpoint {
pub name: String,
pub url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bean {
pub name: String,
pub scope: String,
#[serde(rename = "type")]
pub type_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Logger {
pub name: String,
pub configured_level: Option<String>,
pub effective_level: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Mapping {
pub pattern: String,
pub handler: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvProperty {
pub name: String,
pub value: String,
pub source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub spring_boot_version: Option<String>,
pub java_version: Option<String>,
}
#[derive(Debug, Clone)]
pub struct SavedDump {
pub app_url: String,
pub app_name: String,
pub path: String,
pub timestamp: String,
pub size_bytes: u64,
}
#[derive(Debug, Clone)]
pub struct ThreadInfo {
pub name: String,
pub id: i64,
pub state: String,
pub daemon: bool,
pub stack_trace: Vec<StackFrame>,
}
#[derive(Debug, Clone)]
pub struct StackFrame {
pub class_name: String,
pub method_name: String,
pub file_name: String,
pub line_number: i64,
pub native_method: bool,
}
#[derive(Debug, Clone, Default)]
pub struct HealthComponent {
pub name: String,
pub status: String,
pub details: String,
}
#[derive(Debug, Clone, Default)]
pub struct DashboardData {
pub app_status: String,
pub health_components: Vec<HealthComponent>,
pub heap_used_mb: f64,
pub heap_max_mb: f64,
pub nonheap_used_mb: f64,
pub threads_live: u64,
pub threads_peak: u64,
pub threads_daemon: u64,
pub cpu_system: f64,
pub cpu_process: f64,
pub gc_pause_count: u64,
pub gc_pause_total_ms: f64,
pub http_total_count: u64,
pub http_total_time_s: f64,
pub http_error_count: u64,
pub uptime_seconds: f64,
pub java_version: String,
pub spring_boot_version: String,
pub active_profiles: Vec<String>,
pub disk_free_gb: f64,
pub disk_total_gb: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializrOption {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializrDependency {
pub id: String,
pub name: String,
#[serde(default)]
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializrDependencyGroup {
pub name: String,
pub values: Vec<InitializrDependency>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct InitializrTextField {
pub default: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializrMetadata {
pub boot_versions: Vec<InitializrOption>,
pub boot_version_default: String,
pub languages: Vec<InitializrOption>,
pub language_default: String,
pub packagings: Vec<InitializrOption>,
pub packaging_default: String,
pub java_versions: Vec<InitializrOption>,
pub java_version_default: String,
pub project_types: Vec<InitializrOption>,
pub project_type_default: String,
pub dependency_groups: Vec<InitializrDependencyGroup>,
pub group_id_default: String,
pub artifact_id_default: String,
pub version_default: String,
pub name_default: String,
pub description_default: String,
pub package_name_default: String,
}
#[derive(Debug, Clone)]
pub struct NewProjectParams {
pub boot_version: String,
pub language: String,
#[allow(dead_code)]
pub packaging: String,
pub java_version: String,
pub project_type: String,
pub group_id: String,
pub artifact_id: String,
#[allow(dead_code)]
pub version: String,
pub name: String,
pub description: String,
pub package_name: String,
pub dependencies: Vec<String>,
pub output_dir: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn app_status_display() {
assert_eq!(AppStatus::Up.to_string(), "UP");
assert_eq!(AppStatus::Down.to_string(), "DOWN");
assert_eq!(AppStatus::Unknown.to_string(), "UNKNOWN");
}
#[test]
fn app_status_default_is_unknown() {
assert_eq!(AppStatus::default(), AppStatus::Unknown);
}
#[test]
fn app_status_serde_roundtrip() {
for status in [AppStatus::Up, AppStatus::Down, AppStatus::Unknown] {
let json = serde_json::to_string(&status).unwrap();
let parsed: AppStatus = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, status);
}
}
#[test]
fn spring_app_serde_roundtrip() {
let app = SpringApp {
name: "my-app".into(),
url: "http://localhost:8080".into(),
status: AppStatus::Up,
};
let json = serde_json::to_string(&app).unwrap();
let parsed: SpringApp = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name, "my-app");
assert_eq!(parsed.url, "http://localhost:8080");
assert_eq!(parsed.status, AppStatus::Up);
}
#[test]
fn bean_serde_rename_type() {
let bean = Bean {
name: "myBean".into(),
scope: "singleton".into(),
type_name: "com.example.MyBean".into(),
};
let json = serde_json::to_string(&bean).unwrap();
assert!(json.contains(r#""type":"com.example.MyBean"#));
let parsed: Bean = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.type_name, "com.example.MyBean");
}
#[test]
fn logger_serde_with_optional_configured_level() {
let logger = Logger {
name: "com.example".into(),
configured_level: None,
effective_level: "INFO".into(),
};
let json = serde_json::to_string(&logger).unwrap();
let parsed: Logger = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.configured_level, None);
assert_eq!(parsed.effective_level, "INFO");
let logger2 = Logger {
configured_level: Some("DEBUG".into()),
..logger
};
let json2 = serde_json::to_string(&logger2).unwrap();
let parsed2: Logger = serde_json::from_str(&json2).unwrap();
assert_eq!(parsed2.configured_level, Some("DEBUG".into()));
}
#[test]
fn dashboard_data_default_all_zero() {
let d = DashboardData::default();
assert_eq!(d.app_status, "");
assert!(d.health_components.is_empty());
assert_eq!(d.heap_used_mb, 0.0);
assert_eq!(d.threads_live, 0);
assert_eq!(d.cpu_system, 0.0);
assert_eq!(d.http_total_count, 0);
assert_eq!(d.uptime_seconds, 0.0);
assert!(d.active_profiles.is_empty());
}
#[test]
fn env_property_serde() {
let prop = EnvProperty {
name: "server.port".into(),
value: "8080".into(),
source: "application.properties".into(),
};
let json = serde_json::to_string(&prop).unwrap();
let parsed: EnvProperty = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name, "server.port");
assert_eq!(parsed.value, "8080");
assert_eq!(parsed.source, "application.properties");
}
#[test]
fn mapping_serde() {
let m = Mapping {
pattern: "/api/users".into(),
handler: "com.example.UserController#list".into(),
};
let json = serde_json::to_string(&m).unwrap();
let parsed: Mapping = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.pattern, "/api/users");
assert_eq!(parsed.handler, "com.example.UserController#list");
}
#[test]
fn initializr_dependency_default_description() {
let json = r#"{"id": "web", "name": "Spring Web"}"#;
let dep: InitializrDependency = serde_json::from_str(json).unwrap();
assert_eq!(dep.id, "web");
assert_eq!(dep.description, ""); }
}