actr_cli/commands/codegen/
metadata.rs1use crate::commands::SupportedLanguage;
2use crate::commands::codegen::proto_model::{MethodModel, ProtoModel, ServiceModel};
3use crate::error::{ActrCliError, Result};
4use actr_protocol::ActrType;
5use serde::{Deserialize, Serialize};
6use std::path::{Path, PathBuf};
7
8pub const ACTR_GEN_META_FILE: &str = "actr-gen-meta.json";
9
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
11pub struct ActrGenMetadata {
12 pub plugin_version: String,
13 pub language: String,
14 #[serde(default)]
15 pub local_services: Vec<LocalServiceMetadata>,
16 #[serde(default)]
17 pub remote_services: Vec<RemoteServiceMetadata>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct LocalServiceMetadata {
22 pub name: String,
23 pub package: String,
24 pub proto_file: String,
25 pub handler_interface: String,
26 pub workload_type: String,
27 pub dispatcher_type: String,
28 pub methods: Vec<MethodMetadata>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct RemoteServiceMetadata {
33 pub name: String,
34 pub package: String,
35 pub proto_file: String,
36 pub actr_type: String,
37 pub client_type: String,
38 pub methods: Vec<MethodMetadata>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct MethodMetadata {
43 pub name: String,
44 pub snake_name: String,
45 pub input_type: String,
46 pub output_type: String,
47 pub route_key: String,
48}
49
50impl ActrGenMetadata {
51 pub fn from_proto_model(language: SupportedLanguage, proto_model: &ProtoModel) -> Self {
52 Self {
53 plugin_version: "actr-cli".to_string(),
54 language: language_key(language).to_string(),
55 local_services: proto_model
56 .local_services
57 .iter()
58 .map(build_local_service_metadata)
59 .collect(),
60 remote_services: proto_model
61 .remote_services
62 .iter()
63 .map(build_remote_service_metadata)
64 .collect(),
65 }
66 }
67}
68
69pub fn metadata_path(output_dir: &Path) -> PathBuf {
70 output_dir.join(ACTR_GEN_META_FILE)
71}
72
73pub fn load_metadata(output_dir: &Path) -> Result<Option<ActrGenMetadata>> {
74 let path = metadata_path(output_dir);
75 if !path.exists() {
76 return Ok(None);
77 }
78
79 let content = std::fs::read_to_string(&path).map_err(|e| {
80 ActrCliError::config_error(format!("Failed to read {}: {e}", path.display()))
81 })?;
82 let metadata = serde_json::from_str(&content).map_err(|e| {
83 ActrCliError::config_error(format!("Failed to parse {}: {e}", path.display()))
84 })?;
85 Ok(Some(metadata))
86}
87
88pub fn write_metadata(output_dir: &Path, metadata: &ActrGenMetadata) -> Result<PathBuf> {
89 std::fs::create_dir_all(output_dir).map_err(|e| {
90 ActrCliError::config_error(format!(
91 "Failed to create metadata output directory {}: {e}",
92 output_dir.display()
93 ))
94 })?;
95
96 let path = metadata_path(output_dir);
97 let content = serde_json::to_string_pretty(metadata)?;
98 std::fs::write(&path, content).map_err(|e| {
99 ActrCliError::config_error(format!("Failed to write {}: {e}", path.display()))
100 })?;
101
102 Ok(path)
103}
104
105fn language_key(language: SupportedLanguage) -> &'static str {
106 match language {
107 SupportedLanguage::Rust => "rust",
108 SupportedLanguage::Python => "python",
109 SupportedLanguage::Swift => "swift",
110 SupportedLanguage::Kotlin => "kotlin",
111 SupportedLanguage::TypeScript => "typescript",
112 }
113}
114
115fn build_local_service_metadata(service: &ServiceModel) -> LocalServiceMetadata {
116 LocalServiceMetadata {
117 name: service.name.clone(),
118 package: service.package.clone(),
119 proto_file: service.relative_path.to_string_lossy().to_string(),
120 handler_interface: format!("{}Handler", service.name),
121 workload_type: format!("{}Workload", service.name),
122 dispatcher_type: format!("{}Dispatcher", service.name),
123 methods: service.methods.iter().map(build_method_metadata).collect(),
124 }
125}
126
127fn build_remote_service_metadata(service: &ServiceModel) -> RemoteServiceMetadata {
128 RemoteServiceMetadata {
129 name: service.name.clone(),
130 package: service.package.clone(),
131 proto_file: service.relative_path.to_string_lossy().to_string(),
132 actr_type: service.actr_type.clone().unwrap_or_else(|| {
133 ActrType {
134 manufacturer: "acme".to_string(),
135 name: service.name.clone(),
136 version: "1.0.0".to_string(),
137 }
138 .to_string_repr()
139 }),
140 client_type: format!("{}Client", service.name),
141 methods: service.methods.iter().map(build_method_metadata).collect(),
142 }
143}
144
145fn build_method_metadata(method: &MethodModel) -> MethodMetadata {
146 MethodMetadata {
147 name: method.name.clone(),
148 snake_name: method.snake_name.clone(),
149 input_type: method.input_type.clone(),
150 output_type: method.output_type.clone(),
151 route_key: method.route_key.clone(),
152 }
153}