use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::typed_id::{LeasedResourceId, SessionId};
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
pub const LEASED_RESOURCES_FEATURE: &str = "leased_resources";
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(rename_all = "snake_case")]
pub enum LeasedResourceStatus {
Active,
Cleaning,
Released,
CleanupFailed,
}
impl std::fmt::Display for LeasedResourceStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Active => write!(f, "active"),
Self::Cleaning => write!(f, "cleaning"),
Self::Released => write!(f, "released"),
Self::CleanupFailed => write!(f, "cleanup_failed"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct LeasedResource {
#[cfg_attr(feature = "openapi", schema(value_type = String, example = "resource_01933b5a00007000800000000000001"))]
pub id: LeasedResourceId,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<String>, example = "session_01933b5a00007000800000000000001"))]
pub session_id: Option<SessionId>,
#[cfg_attr(feature = "openapi", schema(example = "daytona"))]
pub provider: String,
#[cfg_attr(feature = "openapi", schema(example = "sandbox"))]
pub resource_type: String,
#[cfg_attr(feature = "openapi", schema(example = "sbx_a3f1c9d2e8"))]
pub external_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(example = "Customer 42 — Q3 brief"))]
pub display_name: Option<String>,
pub status: LeasedResourceStatus,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(value_type = Option<String>, format = "uuid", example = "550e8400-e29b-41d4-a716-446655440000"))]
pub owner_user_id: Option<Uuid>,
#[cfg_attr(feature = "openapi", schema(example = 900))]
pub lease_duration_seconds: u32,
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:00Z"))]
pub last_touched_at: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:29:00Z"))]
pub lease_expires_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:30:00Z"))]
pub cleanup_started_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:30:04Z"))]
pub cleanup_completed_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "openapi", schema(example = 0))]
pub cleanup_attempts: u32,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "openapi",
schema(example = "daytona.api.timeout: provider call exceeded 10s")
)]
pub last_cleanup_error: Option<String>,
#[serde(default)]
#[cfg_attr(feature = "openapi", schema(value_type = Object))]
pub metadata: serde_json::Value,
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:00:00Z"))]
pub created_at: DateTime<Utc>,
#[cfg_attr(feature = "openapi", schema(example = "2026-05-25T10:14:00Z"))]
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpsertLeasedResource {
pub session_id: SessionId,
pub provider: String,
pub resource_type: String,
pub external_id: String,
pub display_name: Option<String>,
pub owner_user_id: Option<Uuid>,
pub lease_duration_seconds: u32,
#[serde(default)]
pub metadata: serde_json::Value,
}