Skip to main content

tftio_cli_common/
app.rs

1//! Shared CLI application metadata.
2
3use crate::{AgentSurfaceSpec, LicenseType, RepoInfo};
4
5/// Default repository metadata for binaries that ship from the shared tools workspace.
6pub const WORKSPACE_REPO: RepoInfo = RepoInfo::new("tftio-stuff", "tools");
7
8/// Shared metadata for a CLI binary.
9#[derive(Debug, Clone)]
10pub struct ToolSpec {
11    /// Binary name shown in version output and help text.
12    pub bin_name: &'static str,
13    /// Human-readable tool name.
14    pub display_name: &'static str,
15    /// Binary version.
16    pub version: &'static str,
17    /// License rendered by the shared license command.
18    pub license: LicenseType,
19    /// Repository metadata used by update helpers.
20    pub repo: RepoInfo,
21    /// Whether the tool supports JSON output in base commands.
22    pub supports_json: bool,
23    /// Whether the tool exposes doctor checks.
24    pub supports_doctor: bool,
25    /// Whether the tool exposes update support.
26    pub supports_update: bool,
27    /// Declarative agent-mode surface for the tool.
28    pub agent_surface: Option<&'static AgentSurfaceSpec>,
29}
30
31impl ToolSpec {
32    /// Create a new [`ToolSpec`].
33    #[must_use]
34    pub const fn new(
35        bin_name: &'static str,
36        display_name: &'static str,
37        version: &'static str,
38        license: LicenseType,
39        repo: RepoInfo,
40        supports_json: bool,
41        supports_doctor: bool,
42        supports_update: bool,
43    ) -> Self {
44        Self {
45            bin_name,
46            display_name,
47            version,
48            license,
49            repo,
50            supports_json,
51            supports_doctor,
52            supports_update,
53            agent_surface: None,
54        }
55    }
56
57    /// Create a new [`ToolSpec`] using [`WORKSPACE_REPO`].
58    #[must_use]
59    pub const fn workspace(
60        bin_name: &'static str,
61        display_name: &'static str,
62        version: &'static str,
63        license: LicenseType,
64        supports_json: bool,
65        supports_doctor: bool,
66        supports_update: bool,
67    ) -> Self {
68        Self::new(
69            bin_name,
70            display_name,
71            version,
72            license,
73            WORKSPACE_REPO,
74            supports_json,
75            supports_doctor,
76            supports_update,
77        )
78    }
79
80    /// Attach an agent surface to the tool specification.
81    #[must_use]
82    pub const fn with_agent_surface(self, agent_surface: &'static AgentSurfaceSpec) -> Self {
83        Self {
84            agent_surface: Some(agent_surface),
85            ..self
86        }
87    }
88}
89
90/// Create a [`ToolSpec`] for a binary shipped from the shared tools workspace.
91#[must_use]
92pub const fn workspace_tool(
93    bin_name: &'static str,
94    display_name: &'static str,
95    version: &'static str,
96    license: LicenseType,
97    supports_json: bool,
98    supports_doctor: bool,
99    supports_update: bool,
100) -> ToolSpec {
101    ToolSpec::workspace(
102        bin_name,
103        display_name,
104        version,
105        license,
106        supports_json,
107        supports_doctor,
108        supports_update,
109    )
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn tool_spec_new_preserves_fields() {
118        let spec = ToolSpec::new(
119            "tool",
120            "Tool",
121            "1.2.3",
122            LicenseType::MIT,
123            RepoInfo::new("owner", "repo"),
124            true,
125            false,
126            true,
127        );
128
129        assert_eq!(spec.bin_name, "tool");
130        assert_eq!(spec.display_name, "Tool");
131        assert_eq!(spec.version, "1.2.3");
132        assert_eq!(spec.license, LicenseType::MIT);
133        assert_eq!(spec.repo.owner, "owner");
134        assert_eq!(spec.repo.name, "repo");
135        assert!(spec.supports_json);
136        assert!(!spec.supports_doctor);
137        assert!(spec.supports_update);
138        assert!(spec.agent_surface.is_none());
139    }
140
141    #[test]
142    fn workspace_tool_uses_workspace_repo_defaults() {
143        let spec = workspace_tool(
144            "tool",
145            "Tool",
146            "1.2.3",
147            LicenseType::MIT,
148            true,
149            false,
150            false,
151        );
152
153        assert_eq!(spec.repo.owner, WORKSPACE_REPO.owner);
154        assert_eq!(spec.repo.name, WORKSPACE_REPO.name);
155        assert_eq!(spec.bin_name, "tool");
156        assert_eq!(spec.display_name, "Tool");
157    }
158}