agpm_cli/models/mod.rs
1//! Shared data models for AGPM operations
2//!
3//! This module provides reusable data structures that are used across
4//! different CLI commands and core operations, ensuring consistency
5//! and reducing code duplication.
6
7use clap::Args;
8use serde::{Deserialize, Serialize};
9
10/// Common dependency specification used across commands
11#[derive(Debug, Clone, Args)]
12pub struct DependencySpec {
13 /// Dependency specification string
14 ///
15 /// Format: `source:path[@version]` for Git sources or `path` for local files
16 ///
17 /// Git dependency formats:
18 /// • `source:path@version` - Git source with specific version
19 /// • `source:path` - Git source (defaults to "main")
20 ///
21 /// Local dependency formats:
22 /// • `/absolute/path/file.md` - Absolute path
23 /// • `./relative/path/file.md` - Relative path
24 /// • `file:///path/to/file.md` - File URL
25 /// • `C:\Windows\path\file.md` - Windows path
26 ///
27 /// Pattern formats (using glob patterns):
28 /// • `source:agents/*.md@v1.0` - All .md files in agents/
29 /// • `source:agents/**/review*.md` - All review files recursively
30 /// • `./local/**/*.json` - All JSON files recursively
31 ///
32 /// Examples:
33 /// • `official:agents/reviewer.md@v1.0.0`
34 /// • `community:snippets/utils.md`
35 /// • `./agents/local-agent.md`
36 /// • `../shared/resources/hook.json`
37 #[arg(value_name = "SPEC")]
38 pub spec: String,
39
40 /// Custom name for the dependency
41 ///
42 /// If not provided, the name will be derived from the file path.
43 /// This allows for more descriptive or shorter names in the manifest.
44 #[arg(long)]
45 pub name: Option<String>,
46
47 /// Force overwrite if dependency exists
48 ///
49 /// By default, adding a duplicate dependency will fail.
50 /// Use this flag to replace existing dependencies.
51 #[arg(long, short = 'f')]
52 pub force: bool,
53}
54
55/// Arguments for adding an agent dependency
56#[derive(Debug, Clone, Args)]
57pub struct AgentDependency {
58 /// Common dependency specification fields
59 #[command(flatten)]
60 pub common: DependencySpec,
61}
62
63/// Arguments for adding a snippet dependency
64#[derive(Debug, Clone, Args)]
65pub struct SnippetDependency {
66 /// Common dependency specification fields
67 #[command(flatten)]
68 pub common: DependencySpec,
69}
70
71/// Arguments for adding a command dependency
72#[derive(Debug, Clone, Args)]
73pub struct CommandDependency {
74 /// Common dependency specification fields
75 #[command(flatten)]
76 pub common: DependencySpec,
77}
78
79/// Arguments for adding an MCP server dependency
80#[derive(Debug, Clone, Args)]
81pub struct McpServerDependency {
82 /// Common dependency specification fields
83 #[command(flatten)]
84 pub common: DependencySpec,
85}
86
87/// Enum representing all possible dependency types
88#[derive(Debug, Clone)]
89pub enum DependencyType {
90 /// An agent dependency
91 Agent(AgentDependency),
92 /// A snippet dependency
93 Snippet(SnippetDependency),
94 /// A command dependency
95 Command(CommandDependency),
96 /// A script dependency
97 Script(ScriptDependency),
98 /// A hook dependency
99 Hook(HookDependency),
100 /// An MCP server dependency
101 McpServer(McpServerDependency),
102}
103
104impl DependencyType {
105 /// Get the common dependency specification
106 #[must_use]
107 pub const fn common(&self) -> &DependencySpec {
108 match self {
109 Self::Agent(dep) => &dep.common,
110 Self::Snippet(dep) => &dep.common,
111 Self::Command(dep) => &dep.common,
112 Self::Script(dep) => &dep.common,
113 Self::Hook(dep) => &dep.common,
114 Self::McpServer(dep) => &dep.common,
115 }
116 }
117
118 /// Get the resource type as a string
119 #[must_use]
120 pub const fn resource_type(&self) -> &'static str {
121 match self {
122 Self::Agent(_) => "agent",
123 Self::Snippet(_) => "snippet",
124 Self::Command(_) => "command",
125 Self::Script(_) => "script",
126 Self::Hook(_) => "hook",
127 Self::McpServer(_) => "mcp-server",
128 }
129 }
130}
131
132/// Source repository specification
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct SourceSpec {
135 /// Name for the source
136 pub name: String,
137
138 /// Git repository URL
139 pub url: String,
140}
141
142/// Resource installation options
143#[derive(Debug, Clone, Default)]
144pub struct InstallOptions {
145 /// Skip installation, only update lockfile
146 pub no_install: bool,
147
148 /// Force reinstallation even if up to date
149 pub force: bool,
150
151 /// Suppress progress indicators
152 pub quiet: bool,
153
154 /// Use cached data only, don't fetch updates
155 pub offline: bool,
156}
157
158/// Resource update options
159#[derive(Debug, Clone, Default)]
160pub struct UpdateOptions {
161 /// Update all dependencies
162 pub all: bool,
163
164 /// Specific dependencies to update
165 pub dependencies: Vec<String>,
166
167 /// Allow updating to incompatible versions
168 pub breaking: bool,
169
170 /// Suppress progress indicators
171 pub quiet: bool,
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_dependency_type_common() {
180 let agent = DependencyType::Agent(AgentDependency {
181 common: DependencySpec {
182 spec: "test:agent.md".to_string(),
183 name: None,
184 force: false,
185 },
186 });
187
188 assert_eq!(agent.common().spec, "test:agent.md");
189 assert_eq!(agent.resource_type(), "agent");
190 }
191
192 #[test]
193 fn test_mcp_server_dependency() {
194 let mcp = DependencyType::McpServer(McpServerDependency {
195 common: DependencySpec {
196 spec: "test:mcp.toml".to_string(),
197 name: Some("test-server".to_string()),
198 force: true,
199 },
200 });
201
202 assert_eq!(mcp.common().spec, "test:mcp.toml");
203 assert_eq!(mcp.common().name, Some("test-server".to_string()));
204 assert!(mcp.common().force);
205 assert_eq!(mcp.resource_type(), "mcp-server");
206 }
207}
208
209/// Arguments for adding a script dependency
210#[derive(Debug, Clone, Args)]
211pub struct ScriptDependency {
212 /// Common dependency specification fields
213 #[command(flatten)]
214 pub common: DependencySpec,
215}
216
217/// Arguments for adding a hook dependency
218#[derive(Debug, Clone, Args)]
219pub struct HookDependency {
220 /// Common dependency specification fields
221 #[command(flatten)]
222 pub common: DependencySpec,
223}