Skip to main content

zoi_common/
ux.rs

1//! User experience (UX) data structures for Zoi.
2
3use std::collections::BTreeMap;
4
5use serde::Serialize;
6use serde_json::Value;
7
8/// Classifies the source and method used to install a package.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
10pub enum InstallOrigin {
11    /// Package installed from a prebuilt binary in the registry.
12    #[serde(rename = "registry-prebuilt")]
13    RegistryPrebuilt,
14    /// Package built from source in the registry.
15    #[serde(rename = "registry-source")]
16    RegistrySource,
17    /// Package installed from a local archive file.
18    #[serde(rename = "local-archive")]
19    LocalArchive,
20    /// Package installed from a local package definition.
21    #[serde(rename = "local-package")]
22    LocalPackage,
23    /// Package downloaded and installed from a remote URL.
24    #[serde(rename = "url")]
25    RemoteUrl,
26    /// Origin of the package is unknown.
27    #[serde(rename = "unknown")]
28    Unknown
29}
30
31impl InstallOrigin {
32    /// Returns the string representation of the install origin.
33    #[must_use]
34    pub const fn as_str(self) -> &'static str {
35        match self {
36            Self::RegistryPrebuilt => "registry-prebuilt",
37            Self::RegistrySource => "registry-source",
38            Self::LocalArchive => "local-archive",
39            Self::LocalPackage => "local-package",
40            Self::RemoteUrl => "url",
41            Self::Unknown => "unknown"
42        }
43    }
44}
45
46/// A summary of a transaction's results.
47#[derive(Debug, Clone, Serialize)]
48pub struct TransactionSummary {
49    /// The command that was executed.
50    pub command: String,
51    /// Number of successful operations.
52    pub success: usize,
53    /// Number of failed operations.
54    pub failed: usize,
55    /// Number of skipped operations.
56    pub skipped: usize
57}
58
59/// A single row in a preflight summary table.
60#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
61pub struct PreflightRow {
62    /// The key/label for the row.
63    pub key: String,
64    /// The value for the row.
65    pub value: String
66}
67
68/// A summary of preflight checks before an operation.
69#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
70pub struct PreflightSummary {
71    /// The title of the summary.
72    pub title: String,
73    /// The rows containing detailed information.
74    pub rows: Vec<PreflightRow>
75}
76
77impl PreflightSummary {
78    /// Creates a new preflight summary with the given title.
79    pub fn new(title: impl Into<String>) -> Self {
80        Self {
81            title: title.into(),
82            rows: Vec::new()
83        }
84    }
85
86    /// Adds a row to the summary.
87    #[must_use]
88    pub fn row(
89        mut self,
90        key: impl Into<String>,
91        value: impl Into<String>
92    ) -> Self {
93        self.rows.push(PreflightRow {
94            key: key.into(),
95            value: value.into()
96        });
97        self
98    }
99}
100
101/// An item in an explanation report.
102#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
103pub struct ExplainItem {
104    /// The subject of the explanation.
105    pub subject: String,
106    /// The reason or brief explanation.
107    pub reason: String,
108    /// Additional details about the item.
109    #[serde(skip_serializing_if = "Vec::is_empty")]
110    pub details: Vec<String>
111}
112
113/// A report explaining the reasons for certain actions or states.
114#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
115pub struct ExplainReport {
116    /// The title of the report.
117    pub title: String,
118    /// The items in the report.
119    pub items: Vec<ExplainItem>
120}
121
122impl ExplainReport {
123    /// Creates a new explanation report with the given title.
124    pub fn new(title: impl Into<String>) -> Self {
125        Self {
126            title: title.into(),
127            items: Vec::new()
128        }
129    }
130
131    /// Adds an item to the report.
132    #[must_use]
133    pub fn item(
134        mut self,
135        subject: impl Into<String>,
136        reason: impl Into<String>,
137        details: Vec<String>
138    ) -> Self {
139        self.items.push(ExplainItem {
140            subject: subject.into(),
141            reason: reason.into(),
142            details
143        });
144        self
145    }
146}
147
148/// The standard JSON schema for Zoi execution plans.
149#[derive(Debug, Clone, Serialize, PartialEq)]
150pub struct PlanJsonV1 {
151    /// Schema version (currently "zoi.plan.v1").
152    pub schema: String,
153    /// The command that generated this plan (e.g. "install", "update").
154    pub command: String,
155    /// Command-specific fields.
156    #[serde(flatten)]
157    pub fields: BTreeMap<String, Value>
158}
159
160impl PlanJsonV1 {
161    /// Creates a new version 1 plan JSON object.
162    pub fn new(
163        command: impl Into<String>,
164        fields: BTreeMap<String, Value>
165    ) -> Self {
166        Self {
167            schema: "zoi.plan.v1".to_string(),
168            command: command.into(),
169            fields
170        }
171    }
172}