Skip to main content

acp_http_adapter/
registry.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use serde::Deserialize;
5use serde_json::Value;
6use thiserror::Error;
7
8#[derive(Debug, Clone)]
9pub struct LaunchSpec {
10    pub program: PathBuf,
11    pub args: Vec<String>,
12    pub env: HashMap<String, String>,
13}
14
15#[derive(Debug, Error)]
16pub enum RegistryError {
17    #[error("invalid registry json: {0}")]
18    InvalidJson(#[from] serde_json::Error),
19    #[error("unable to resolve registry entry from blob")]
20    UnsupportedBlob,
21    #[error("registry blob has agents[] but no --registry-agent-id was provided")]
22    MissingAgentId,
23    #[error("agent '{0}' was not found in registry blob")]
24    AgentNotFound(String),
25    #[error("registry entry has no supported launch target")]
26    MissingLaunchTarget,
27    #[error("platform '{0}' is not present in distribution.binary")]
28    UnsupportedPlatform(String),
29}
30
31impl LaunchSpec {
32    pub fn from_registry_blob(blob: &str, agent_id: Option<&str>) -> Result<Self, RegistryError> {
33        let value: Value = serde_json::from_str(blob)?;
34        Self::from_registry_value(value, agent_id)
35    }
36
37    fn from_registry_value(value: Value, agent_id: Option<&str>) -> Result<Self, RegistryError> {
38        if value.get("agents").is_some() {
39            let doc: RegistryDocument = serde_json::from_value(value)?;
40            let wanted = agent_id.ok_or(RegistryError::MissingAgentId)?;
41            let agent = doc
42                .agents
43                .into_iter()
44                .find(|a| a.id == wanted)
45                .ok_or_else(|| RegistryError::AgentNotFound(wanted.to_string()))?;
46            return Self::from_distribution(agent.distribution);
47        }
48
49        if value.get("distribution").is_some() {
50            let entry: RegistryAgent = serde_json::from_value(value)?;
51            return Self::from_distribution(entry.distribution);
52        }
53
54        if value.get("npx").is_some() || value.get("binary").is_some() {
55            let distribution: RegistryDistribution = serde_json::from_value(value)?;
56            return Self::from_distribution(distribution);
57        }
58
59        Err(RegistryError::UnsupportedBlob)
60    }
61
62    fn from_distribution(distribution: RegistryDistribution) -> Result<Self, RegistryError> {
63        if let Some(npx) = distribution.npx {
64            let mut args = vec!["-y".to_string(), npx.package];
65            args.extend(npx.args);
66            return Ok(Self {
67                program: PathBuf::from("npx"),
68                args,
69                env: npx.env,
70            });
71        }
72
73        if let Some(binary) = distribution.binary {
74            let platform = platform_key().ok_or(RegistryError::UnsupportedPlatform(format!(
75                "{}/{}",
76                std::env::consts::OS,
77                std::env::consts::ARCH
78            )))?;
79            let target = binary
80                .get(platform)
81                .ok_or_else(|| RegistryError::UnsupportedPlatform(platform.to_string()))?;
82            return Ok(Self {
83                program: PathBuf::from(&target.cmd),
84                args: target.args.clone(),
85                env: target.env.clone(),
86            });
87        }
88
89        Err(RegistryError::MissingLaunchTarget)
90    }
91}
92
93fn platform_key() -> Option<&'static str> {
94    match (std::env::consts::OS, std::env::consts::ARCH) {
95        ("linux", "x86_64") => Some("linux-x86_64"),
96        ("linux", "aarch64") => Some("linux-aarch64"),
97        ("macos", "x86_64") => Some("darwin-x86_64"),
98        ("macos", "aarch64") => Some("darwin-aarch64"),
99        ("windows", "x86_64") => Some("windows-x86_64"),
100        ("windows", "aarch64") => Some("windows-aarch64"),
101        _ => None,
102    }
103}
104
105#[derive(Debug, Deserialize)]
106struct RegistryDocument {
107    agents: Vec<RegistryAgent>,
108}
109
110#[derive(Debug, Deserialize)]
111struct RegistryAgent {
112    #[allow(dead_code)]
113    id: String,
114    distribution: RegistryDistribution,
115}
116
117#[derive(Debug, Deserialize)]
118struct RegistryDistribution {
119    #[serde(default)]
120    npx: Option<RegistryNpx>,
121    #[serde(default)]
122    binary: Option<HashMap<String, RegistryBinaryTarget>>,
123}
124
125#[derive(Debug, Deserialize)]
126struct RegistryNpx {
127    package: String,
128    #[serde(default)]
129    args: Vec<String>,
130    #[serde(default)]
131    env: HashMap<String, String>,
132}
133
134#[derive(Debug, Deserialize)]
135struct RegistryBinaryTarget {
136    #[allow(dead_code)]
137    archive: Option<String>,
138    cmd: String,
139    #[serde(default)]
140    args: Vec<String>,
141    #[serde(default)]
142    env: HashMap<String, String>,
143}