r2x_manifest/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// Top-level manifest structure for R2X plugin metadata
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Manifest {
6    pub metadata: Metadata,
7    #[serde(default)]
8    pub packages: Vec<Package>,
9}
10
11/// Manifest metadata - version and generation info
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Metadata {
14    /// Schema version for future compatibility
15    pub version: String,
16    /// ISO 8601 timestamp when manifest was generated
17    pub generated_at: String,
18    /// Optional path to UV's lock file for version/dependency info
19    pub uv_lock_path: Option<String>,
20}
21
22impl Default for Manifest {
23    fn default() -> Self {
24        Manifest {
25            metadata: Metadata {
26                version: "2.0".to_string(),
27                generated_at: chrono::Utc::now().to_rfc3339(),
28                uv_lock_path: None,
29            },
30            packages: Vec::new(),
31        }
32    }
33}
34
35/// Represents a single Python package containing r2x plugins
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct Package {
38    pub name: String,
39    pub entry_points_dist_info: String,
40    #[serde(default)]
41    pub editable_install: bool,
42    pub pth_file: Option<String>,
43    pub resolved_source_path: Option<String>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub install_type: Option<String>,
46    #[serde(default)]
47    #[serde(skip_serializing_if = "Vec::is_empty")]
48    pub installed_by: Vec<String>,
49    #[serde(default)]
50    #[serde(skip_serializing_if = "Vec::is_empty")]
51    pub dependencies: Vec<String>,
52    #[serde(default)]
53    pub plugins: Vec<PluginSpec>,
54    #[serde(default)]
55    pub decorator_registrations: Vec<DecoratorRegistration>,
56}
57
58/// Complete plugin specification matching R2X-core's PluginSpec
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct PluginSpec {
61    pub name: String,
62    pub kind: PluginKind,
63    pub entry: String,
64    pub invocation: InvocationSpec,
65    pub io: IOContract,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub resources: Option<ResourceSpec>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub upgrade: Option<UpgradeSpec>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub description: Option<String>,
72    #[serde(default)]
73    #[serde(skip_serializing_if = "Vec::is_empty")]
74    pub tags: Vec<String>,
75}
76
77/// Plugin kind/type enumeration
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
79#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
80pub enum PluginKind {
81    Parser,
82    Exporter,
83    Modifier,
84    Upgrader,
85    Utility,
86}
87
88/// How to construct and invoke a plugin
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct InvocationSpec {
91    pub implementation: ImplementationType,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub method: Option<String>,
94    #[serde(default)]
95    #[serde(skip_serializing_if = "Vec::is_empty")]
96    pub constructor: Vec<ArgumentSpec>,
97    #[serde(default)]
98    #[serde(skip_serializing_if = "Vec::is_empty")]
99    pub call: Vec<ArgumentSpec>,
100}
101
102/// Implementation type for plugins
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
104#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
105pub enum ImplementationType {
106    Class,
107    Function,
108}
109
110/// Argument specification for constructor or call
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ArgumentSpec {
113    pub name: String,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub annotation: Option<String>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub default: Option<String>,
118    pub required: bool,
119}
120
121/// Input/output contract for a plugin
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct IOContract {
124    #[serde(default)]
125    #[serde(skip_serializing_if = "Vec::is_empty")]
126    pub consumes: Vec<IOSlot>,
127    #[serde(default)]
128    #[serde(skip_serializing_if = "Vec::is_empty")]
129    pub produces: Vec<IOSlot>,
130}
131
132/// I/O slot type
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
134#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
135pub enum IOSlot {
136    System,
137    ConfigFile,
138    StoreFolder,
139    File,
140    Folder,
141    Data,
142}
143
144/// Resource requirements (config and data store)
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct ResourceSpec {
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub store: Option<StoreSpec>,
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub config: Option<ConfigSpec>,
151}
152
153/// Data store specification
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct StoreSpec {
156    pub mode: StoreMode,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub path: Option<String>,
159}
160
161/// Data store mode
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
163#[serde(rename_all = "lowercase")]
164pub enum StoreMode {
165    Folder,
166    Manifest,
167    Inline,
168}
169
170/// Configuration specification
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct ConfigSpec {
173    pub module: String,
174    pub name: String,
175    #[serde(default)]
176    #[serde(skip_serializing_if = "Vec::is_empty")]
177    pub fields: Vec<ConfigField>,
178}
179
180/// Configuration field specification
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct ConfigField {
183    pub name: String,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub annotation: Option<String>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub default: Option<String>,
188    pub required: bool,
189}
190
191/// Upgrade specification for upgrader plugins
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct UpgradeSpec {
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub version_strategy_json: Option<String>,
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub version_reader_json: Option<String>,
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub upgrade_steps_json: Option<String>,
200}
201
202/// Function registration via decorator
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct DecoratorRegistration {
205    pub decorator_class: String,
206    pub decorator_method: String,
207    pub function_name: String,
208    pub function_module: String,
209    pub source_file: Option<String>,
210    pub line_number: Option<usize>,
211    #[serde(default)]
212    pub decorator_args: toml::Table,
213    pub function_signature: Option<FunctionSignature>,
214}
215
216/// Complete function signature extracted from source
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct FunctionSignature {
219    pub return_type: String,
220    #[serde(default)]
221    pub parameters: Vec<FunctionParameter>,
222}
223
224/// Single function parameter
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct FunctionParameter {
227    pub name: String,
228    pub param_type: String,
229    pub default: Option<String>,
230    #[serde(default)]
231    pub is_keyword_only: bool,
232    pub is_var_arg: Option<VarArgType>,
233}
234
235/// Type of variable argument (*args or **kwargs)
236#[derive(Debug, Clone, Serialize, Deserialize)]
237#[serde(rename_all = "lowercase")]
238pub enum VarArgType {
239    Args,
240    Kwargs,
241}