use std::{
collections::BTreeMap,
env,
error::Error,
fmt::Write as _,
fs,
path::{Path, PathBuf},
};
use serde::Deserialize;
const DEFAULT_REGISTRY_URL: &str =
"https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
const DEFAULT_OUTPUT_PATH: &str = "src/agent_servers.rs";
#[derive(Debug, Deserialize)]
struct RegistryDocument {
version: String,
agents: Vec<RegistryAgent>,
}
#[derive(Clone, Debug, Deserialize)]
struct RegistryAgent {
id: String,
name: String,
description: String,
version: String,
repository: Option<String>,
authors: Vec<String>,
license: String,
icon: Option<String>,
distribution: RegistryDistribution,
}
#[derive(Clone, Debug, Default, Deserialize)]
struct RegistryDistribution {
#[serde(default)]
binary: BTreeMap<String, BinaryTarget>,
#[serde(default)]
npx: Option<PackageDistribution>,
#[serde(default)]
uvx: Option<PackageDistribution>,
}
#[derive(Clone, Debug, Deserialize)]
struct BinaryTarget {
archive: String,
cmd: String,
}
#[derive(Clone, Debug, Deserialize)]
struct PackageDistribution {
package: String,
#[serde(default)]
args: Vec<String>,
#[serde(default)]
env: BTreeMap<String, String>,
}
fn main() -> Result<(), Box<dyn Error>> {
let args = env::args().skip(1).collect::<Vec<_>>();
if args.len() > 2 {
return Err("usage: registry-sync [registry-json-path-or-url] [output-path]".into());
}
let input = args
.first()
.map_or_else(|| DEFAULT_REGISTRY_URL.to_owned(), Clone::clone);
let output = args
.get(1)
.map_or_else(|| PathBuf::from(DEFAULT_OUTPUT_PATH), PathBuf::from);
let registry = load_registry(&input)?;
let rendered = render_registry_module(®istry)?;
write_if_changed(&output, &rendered)?;
println!(
"synced {} agents from {} into {}",
registry.agents.len(),
input,
output.display()
);
Ok(())
}
fn load_registry(input: &str) -> Result<RegistryDocument, Box<dyn Error>> {
let raw = if input.starts_with("https://") || input.starts_with("http://") {
ureq::get(input).call()?.into_string()?
} else {
fs::read_to_string(input)?
};
Ok(serde_json::from_str(&raw)?)
}
fn write_if_changed(path: &Path, rendered: &str) -> Result<(), Box<dyn Error>> {
match fs::read_to_string(path) {
Ok(existing) if existing == rendered => Ok(()),
Ok(_) | Err(_) => {
fs::write(path, rendered)?;
Ok(())
}
}
}
fn render_registry_module(registry: &RegistryDocument) -> Result<String, Box<dyn Error>> {
let mut agents = registry.agents.clone();
agents.sort_by(|left, right| left.id.cmp(&right.id));
let mut rendered = String::new();
render_module_prelude(&mut rendered)?;
render_error_type(&mut rendered)?;
render_host_platform_type(&mut rendered)?;
render_generated_agent_server_type(&mut rendered)?;
render_generated_distribution_types(&mut rendered)?;
render_registry_version(&mut rendered, ®istry.version)?;
for agent in &agents {
render_agent_function(&mut rendered, agent)?;
}
render_registry_functions(&mut rendered, &agents)?;
render_public_helpers(&mut rendered)?;
Ok(rendered)
}
fn render_module_prelude(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(
rendered,
"// This file is @generated by `just registry-sync`."
)?;
writeln!(rendered, "// Do not edit manually.")?;
writeln!(rendered)?;
writeln!(rendered, "use std::env;")?;
writeln!(rendered)?;
writeln!(rendered, "use thiserror::Error;")?;
writeln!(rendered)?;
writeln!(rendered, "use crate::{{")?;
writeln!(
rendered,
" agent_server::{{AgentServerMetadata, AgentServer, CommandAgentServer, CommandSpec}},"
)?;
writeln!(
rendered,
" Connection, Result, RuntimeContext, Task, UnsupportedLaunch,"
)?;
writeln!(rendered, "}};")?;
writeln!(rendered)?;
Ok(())
}
fn render_error_type(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(
rendered,
"/// Typed failures from agent-server lookup and host-platform helpers."
)?;
writeln!(rendered, "#[derive(Clone, Debug, PartialEq, Eq, Error)]")?;
writeln!(rendered, "pub enum Error {{")?;
writeln!(
rendered,
" /// The generated agent-server catalog does not contain the requested id."
)?;
writeln!(
rendered,
" #[error(\"agent server `{{id}}` was not found\")]"
)?;
writeln!(rendered, " UnknownServer {{")?;
writeln!(
rendered,
" /// The unresolved official ACP registry id."
)?;
writeln!(rendered, " id: String,")?;
writeln!(rendered, " }},")?;
writeln!(rendered)?;
writeln!(
rendered,
" /// The current or requested host target is outside the v0 platform map."
)?;
writeln!(
rendered,
" #[error(\"unsupported host platform `{{os}}/{{arch}}`\")]"
)?;
writeln!(rendered, " UnsupportedHostPlatform {{")?;
writeln!(rendered, " /// The operating system identifier.")?;
writeln!(rendered, " os: String,")?;
writeln!(rendered, " /// The CPU architecture identifier.")?;
writeln!(rendered, " arch: String,")?;
writeln!(rendered, " }},")?;
writeln!(rendered)?;
writeln!(
rendered,
" /// A binary-backed server does not provide a build for the requested host"
)?;
writeln!(rendered, " /// target.")?;
writeln!(
rendered,
" #[error(\"agent server `{{id}}` does not publish a binary for host platform `{{target}}`\")]"
)?;
writeln!(rendered, " MissingBinaryTarget {{")?;
writeln!(rendered, " /// The agent-server id.")?;
writeln!(rendered, " id: String,")?;
writeln!(
rendered,
" /// The official ACP registry target triple."
)?;
writeln!(rendered, " target: String,")?;
writeln!(rendered, " }},")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
#[allow(clippy::too_many_lines)]
fn render_host_platform_type(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(
rendered,
"/// Host platforms currently mapped to ACP registry binary targets."
)?;
writeln!(rendered, "#[derive(Clone, Copy, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub enum HostPlatform {{")?;
writeln!(rendered, " DarwinAarch64,")?;
writeln!(rendered, " DarwinX86_64,")?;
writeln!(rendered, " LinuxAarch64,")?;
writeln!(rendered, " LinuxX86_64,")?;
writeln!(rendered, " WindowsAarch64,")?;
writeln!(rendered, " WindowsX86_64,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl HostPlatform {{")?;
writeln!(
rendered,
" /// Resolves a Rust target OS and architecture into an ACP registry target."
)?;
writeln!(rendered, " ///")?;
writeln!(rendered, " /// # Errors")?;
writeln!(rendered, " ///")?;
writeln!(
rendered,
" /// Returns [`Error::UnsupportedHostPlatform`] when the pair does not map"
)?;
writeln!(
rendered,
" /// to a supported ACP registry target in v0."
)?;
writeln!(
rendered,
" pub fn from_target(os: &str, arch: &str) -> Result<Self, Error> {{"
)?;
writeln!(rendered, " match (os, arch) {{")?;
writeln!(
rendered,
" (\"macos\", \"aarch64\") => Ok(Self::DarwinAarch64),"
)?;
writeln!(
rendered,
" (\"macos\", \"x86_64\") => Ok(Self::DarwinX86_64),"
)?;
writeln!(
rendered,
" (\"linux\", \"aarch64\") => Ok(Self::LinuxAarch64),"
)?;
writeln!(
rendered,
" (\"linux\", \"x86_64\") => Ok(Self::LinuxX86_64),"
)?;
writeln!(
rendered,
" (\"windows\", \"aarch64\") => Ok(Self::WindowsAarch64),"
)?;
writeln!(
rendered,
" (\"windows\", \"x86_64\") => Ok(Self::WindowsX86_64),"
)?;
writeln!(
rendered,
" _ => Err(Error::UnsupportedHostPlatform {{"
)?;
writeln!(rendered, " os: os.to_owned(),")?;
writeln!(rendered, " arch: arch.to_owned(),")?;
writeln!(rendered, " }}),")?;
writeln!(rendered, " }}")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(
rendered,
" /// Returns the official ACP registry target identifier."
)?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn registry_target(self) -> &'static str {{"
)?;
writeln!(rendered, " match self {{")?;
writeln!(
rendered,
" Self::DarwinAarch64 => \"darwin-aarch64\","
)?;
writeln!(
rendered,
" Self::DarwinX86_64 => \"darwin-x86_64\","
)?;
writeln!(
rendered,
" Self::LinuxAarch64 => \"linux-aarch64\","
)?;
writeln!(
rendered,
" Self::LinuxX86_64 => \"linux-x86_64\","
)?;
writeln!(
rendered,
" Self::WindowsAarch64 => \"windows-aarch64\","
)?;
writeln!(
rendered,
" Self::WindowsX86_64 => \"windows-x86_64\","
)?;
writeln!(rendered, " }}")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_generated_agent_server_type(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[derive(Clone, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub struct Server {{")?;
writeln!(rendered, " metadata: AgentServerMetadata,")?;
writeln!(rendered, " repository: Option<String>,")?;
writeln!(rendered, " authors: Vec<String>,")?;
writeln!(rendered, " license: String,")?;
writeln!(rendered, " distribution: Distribution,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl Server {{")?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn repository(&self) -> Option<&str> {{")?;
writeln!(rendered, " self.repository.as_deref()")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn authors(&self) -> &[String] {{")?;
writeln!(rendered, " &self.authors")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn license(&self) -> &str {{")?;
writeln!(rendered, " &self.license")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn distribution(&self) -> &Distribution {{"
)?;
writeln!(rendered, " &self.distribution")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl AgentServer for Server {{")?;
writeln!(
rendered,
" fn metadata(&self) -> &AgentServerMetadata {{"
)?;
writeln!(rendered, " &self.metadata")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(
rendered,
" fn connect<'a>(&'a self, runtime: &'a RuntimeContext) -> Task<'a, Result<Connection>> {{"
)?;
writeln!(rendered, " Box::pin(async move {{")?;
writeln!(
rendered,
" let Some(package) = self.distribution.preferred_package() else {{"
)?;
writeln!(
rendered,
" return Err(UnsupportedLaunch::BinaryDistribution.into());"
)?;
writeln!(rendered, " }};")?;
writeln!(
rendered,
" let manual = CommandAgentServer::new(self.metadata.clone(), package.command_spec());"
)?;
writeln!(rendered, " manual.connect(runtime).await")?;
writeln!(rendered, " }})")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_generated_distribution_types(rendered: &mut String) -> Result<(), Box<dyn Error>> {
render_generated_distribution_struct(rendered)?;
render_generated_binary_target_type(rendered)?;
render_generated_package_distribution_types(rendered)?;
Ok(())
}
fn render_generated_distribution_struct(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[derive(Clone, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub struct Distribution {{")?;
writeln!(rendered, " binary: Vec<BinaryTarget>,")?;
writeln!(rendered, " npx: Option<PackageDistribution>,")?;
writeln!(rendered, " uvx: Option<PackageDistribution>,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl Distribution {{")?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn binary_targets(&self) -> &[BinaryTarget] {{"
)?;
writeln!(rendered, " &self.binary")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn npx(&self) -> Option<&PackageDistribution> {{"
)?;
writeln!(rendered, " self.npx.as_ref()")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn uvx(&self) -> Option<&PackageDistribution> {{"
)?;
writeln!(rendered, " self.uvx.as_ref()")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(
rendered,
" fn preferred_package(&self) -> Option<&PackageDistribution> {{"
)?;
writeln!(rendered, " self.npx.as_ref().or(self.uvx.as_ref())")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_generated_binary_target_type(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[derive(Clone, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub struct BinaryTarget {{")?;
writeln!(rendered, " target: String,")?;
writeln!(rendered, " archive: String,")?;
writeln!(rendered, " cmd: String,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl BinaryTarget {{")?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn target(&self) -> &str {{")?;
writeln!(rendered, " &self.target")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn archive(&self) -> &str {{")?;
writeln!(rendered, " &self.archive")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn cmd(&self) -> &str {{")?;
writeln!(rendered, " &self.cmd")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_generated_package_distribution_types(
rendered: &mut String,
) -> Result<(), Box<dyn Error>> {
render_generated_package_manager_type(rendered)?;
render_generated_package_distribution_struct(rendered)?;
Ok(())
}
fn render_generated_package_manager_type(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[derive(Clone, Copy, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub enum PackageManager {{")?;
writeln!(rendered, " Npx,")?;
writeln!(rendered, " Uvx,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl PackageManager {{")?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn launcher(self) -> &'static str {{")?;
writeln!(rendered, " match self {{")?;
writeln!(rendered, " Self::Npx => \"npx\",")?;
writeln!(rendered, " Self::Uvx => \"uvx\",")?;
writeln!(rendered, " }}")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(
rendered,
" pub fn launcher_args(self) -> &'static [&'static str] {{"
)?;
writeln!(rendered, " match self {{")?;
writeln!(rendered, " Self::Npx => &[\"--yes\"],")?;
writeln!(rendered, " Self::Uvx => &[],")?;
writeln!(rendered, " }}")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_generated_package_distribution_struct(
rendered: &mut String,
) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[derive(Clone, Debug, PartialEq, Eq)]")?;
writeln!(rendered, "pub struct PackageDistribution {{")?;
writeln!(rendered, " manager: PackageManager,")?;
writeln!(rendered, " package: String,")?;
writeln!(rendered, " args: Vec<String>,")?;
writeln!(rendered, " env: Vec<(String, String)>,")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "impl PackageDistribution {{")?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn manager(&self) -> PackageManager {{")?;
writeln!(rendered, " self.manager")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn package(&self) -> &str {{")?;
writeln!(rendered, " &self.package")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn args(&self) -> &[String] {{")?;
writeln!(rendered, " &self.args")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn env(&self) -> &[(String, String)] {{")?;
writeln!(rendered, " &self.env")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " #[must_use]")?;
writeln!(rendered, " pub fn command_spec(&self) -> CommandSpec {{")?;
writeln!(
rendered,
" let mut spec = CommandSpec::new(self.manager.launcher());"
)?;
writeln!(
rendered,
" for arg in self.manager.launcher_args() {{"
)?;
writeln!(rendered, " spec = spec.arg(*arg);")?;
writeln!(rendered, " }}")?;
writeln!(rendered, " spec = spec.arg(self.package.clone());")?;
writeln!(rendered, " for arg in &self.args {{")?;
writeln!(rendered, " spec = spec.arg(arg.clone());")?;
writeln!(rendered, " }}")?;
writeln!(rendered, " for (key, value) in &self.env {{")?;
writeln!(
rendered,
" spec = spec.env(key.clone(), value.clone());"
)?;
writeln!(rendered, " }}")?;
writeln!(rendered, " spec")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_registry_version(rendered: &mut String, version: &str) -> Result<(), Box<dyn Error>> {
writeln!(
rendered,
"pub const VERSION: &str = {};",
rust_string_literal(version)
)?;
writeln!(rendered)?;
Ok(())
}
fn render_registry_functions(
rendered: &mut String,
agents: &[RegistryAgent],
) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[must_use]")?;
writeln!(rendered, "pub fn all() -> Vec<Server> {{")?;
writeln!(rendered, " vec![")?;
for agent in agents {
writeln!(rendered, " {}(),", function_name(&agent.id))?;
}
writeln!(rendered, " ]")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(rendered, "#[must_use]")?;
writeln!(rendered, "pub fn get(id: &str) -> Option<Server> {{")?;
writeln!(rendered, " match id {{")?;
for agent in agents {
writeln!(
rendered,
" {} => Some({}()),",
rust_string_literal(&agent.id),
function_name(&agent.id)
)?;
}
writeln!(rendered, " _ => None,")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
Ok(())
}
#[allow(clippy::too_many_lines)]
fn render_public_helpers(rendered: &mut String) -> Result<(), Box<dyn Error>> {
writeln!(rendered)?;
writeln!(
rendered,
"/// Returns the embedded agent-server catalog version."
)?;
writeln!(rendered, "#[must_use]")?;
writeln!(rendered, "pub fn version() -> &'static str {{")?;
writeln!(rendered, " VERSION")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(
rendered,
"/// Resolves an official ACP registry id and returns a typed lookup error on"
)?;
writeln!(rendered, "/// failure.")?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// # Errors")?;
writeln!(rendered, "///")?;
writeln!(
rendered,
"/// Returns [`Error::UnknownServer`] when the generated catalog does not"
)?;
writeln!(rendered, "/// contain the requested id.")?;
writeln!(
rendered,
"pub fn require(id: &str) -> Result<Server, Error> {{"
)?;
writeln!(
rendered,
" get(id).ok_or_else(|| Error::UnknownServer {{ id: id.to_owned() }})"
)?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(
rendered,
"/// Resolves the current host into a known ACP registry target."
)?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// # Errors")?;
writeln!(rendered, "///")?;
writeln!(
rendered,
"/// Returns [`Error::UnsupportedHostPlatform`] when the build host does not map"
)?;
writeln!(rendered, "/// to a supported registry target in v0.")?;
writeln!(
rendered,
"pub fn host_platform() -> Result<HostPlatform, Error> {{"
)?;
writeln!(
rendered,
" HostPlatform::from_target(env::consts::OS, env::consts::ARCH)"
)?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(
rendered,
"/// Resolves the binary target for the current host when the server publishes"
)?;
writeln!(rendered, "/// binaries.")?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// Package-backed servers return `Ok(None)`.")?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// # Errors")?;
writeln!(rendered, "///")?;
writeln!(
rendered,
"/// Returns [`Error::UnsupportedHostPlatform`] when the current host is not"
)?;
writeln!(
rendered,
"/// mapped in v0, or [`Error::MissingBinaryTarget`] when the server publishes"
)?;
writeln!(
rendered,
"/// binaries but not for the current host target."
)?;
writeln!(
rendered,
"pub fn host_binary_target(server: &Server) -> Result<Option<&BinaryTarget>, Error> {{"
)?;
writeln!(rendered, " binary_target_for(server, host_platform()?)")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
writeln!(
rendered,
"/// Resolves the binary target for a specific host platform when the server"
)?;
writeln!(rendered, "/// publishes binaries.")?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// Package-backed servers return `Ok(None)`.")?;
writeln!(rendered, "///")?;
writeln!(rendered, "/// # Errors")?;
writeln!(rendered, "///")?;
writeln!(
rendered,
"/// Returns [`Error::MissingBinaryTarget`] when the server publishes binaries"
)?;
writeln!(rendered, "/// but not for the requested host target.")?;
writeln!(rendered, "pub fn binary_target_for(")?;
writeln!(rendered, " server: &Server,")?;
writeln!(rendered, " platform: HostPlatform,")?;
writeln!(rendered, ") -> Result<Option<&BinaryTarget>, Error> {{")?;
writeln!(
rendered,
" let binary_targets = server.distribution().binary_targets();"
)?;
writeln!(rendered, " if binary_targets.is_empty() {{")?;
writeln!(rendered, " return Ok(None);")?;
writeln!(rendered, " }}")?;
writeln!(rendered)?;
writeln!(rendered, " binary_targets")?;
writeln!(rendered, " .iter()")?;
writeln!(
rendered,
" .find(|target| target.target() == platform.registry_target())"
)?;
writeln!(rendered, " .map_or_else(")?;
writeln!(rendered, " || {{")?;
writeln!(
rendered,
" Err(Error::MissingBinaryTarget {{"
)?;
writeln!(rendered, " id: server.id().to_owned(),")?;
writeln!(
rendered,
" target: platform.registry_target().to_owned(),"
)?;
writeln!(rendered, " }})")?;
writeln!(rendered, " }},")?;
writeln!(rendered, " |target| Ok(Some(target)),")?;
writeln!(rendered, " )")?;
writeln!(rendered, "}}")?;
Ok(())
}
fn render_agent_function(
rendered: &mut String,
agent: &RegistryAgent,
) -> Result<(), Box<dyn Error>> {
writeln!(rendered, "#[must_use]")?;
writeln!(
rendered,
"pub fn {}() -> Server {{",
function_name(&agent.id)
)?;
writeln!(rendered, " Server {{")?;
write!(
rendered,
" metadata: AgentServerMetadata::new({}, {}, {})",
rust_string_literal(&agent.id),
rust_string_literal(&agent.name),
rust_string_literal(&agent.version)
)?;
if !agent.description.is_empty() {
write!(
rendered,
".description({})",
rust_string_literal(&agent.description)
)?;
}
if let Some(icon) = &agent.icon {
write!(rendered, ".icon({})", rust_string_literal(icon))?;
}
writeln!(rendered, ",")?;
writeln!(
rendered,
" repository: {},",
render_optional_owned_string(agent.repository.as_deref())
)?;
writeln!(
rendered,
" authors: vec![{}],",
render_string_list(&agent.authors)
)?;
writeln!(
rendered,
" license: {}.to_owned(),",
rust_string_literal(&agent.license)
)?;
writeln!(rendered, " distribution: Distribution {{")?;
writeln!(
rendered,
" binary: vec![{}],",
render_binary_targets(&agent.distribution.binary)
)?;
writeln!(
rendered,
" npx: {},",
render_package_distribution(agent.distribution.npx.as_ref(), "Npx")
)?;
writeln!(
rendered,
" uvx: {},",
render_package_distribution(agent.distribution.uvx.as_ref(), "Uvx")
)?;
writeln!(rendered, " }},")?;
writeln!(rendered, " }}")?;
writeln!(rendered, "}}")?;
writeln!(rendered)?;
Ok(())
}
fn render_binary_targets(binary: &BTreeMap<String, BinaryTarget>) -> String {
let mut rendered = Vec::new();
for (target, details) in binary {
rendered.push(format!(
"BinaryTarget {{ target: {}.to_owned(), archive: {}.to_owned(), cmd: {}.to_owned() }}",
rust_string_literal(target),
rust_string_literal(&details.archive),
rust_string_literal(&details.cmd),
));
}
rendered.join(", ")
}
fn render_package_distribution(package: Option<&PackageDistribution>, manager: &str) -> String {
let Some(package) = package else {
return "None".to_owned();
};
let args = render_string_list(&package.args);
let env = package
.env
.iter()
.map(|(key, value)| {
format!(
"({}.to_owned(), {}.to_owned())",
rust_string_literal(key),
rust_string_literal(value)
)
})
.collect::<Vec<_>>()
.join(", ");
format!(
"Some(PackageDistribution {{ manager: PackageManager::{manager}, package: {}.to_owned(), args: vec![{args}], env: vec![{env}] }})",
rust_string_literal(&package.package)
)
}
fn render_string_list(items: &[String]) -> String {
items
.iter()
.map(|item| format!("{}.to_owned()", rust_string_literal(item)))
.collect::<Vec<_>>()
.join(", ")
}
fn render_optional_owned_string(value: Option<&str>) -> String {
value.map_or_else(
|| "None".to_owned(),
|value| format!("Some({}.to_owned())", rust_string_literal(value)),
)
}
fn function_name(id: &str) -> String {
let mut output = String::with_capacity(id.len() + 8);
output.push_str("agent_");
for ch in id.chars() {
if ch.is_ascii_alphanumeric() {
output.push(ch.to_ascii_lowercase());
} else {
output.push('_');
}
}
output
}
fn rust_string_literal(value: &str) -> String {
format!("{value:?}")
}
#[cfg(test)]
mod tests {
use super::{RegistryDocument, render_registry_module};
#[test]
fn registry_render_is_idempotent_for_sample_fixture() {
let registry: RegistryDocument =
serde_json::from_str(include_str!("../../tests/fixtures/registry-sample.json"))
.expect("fixture should parse");
let first = render_registry_module(®istry).expect("first render should succeed");
let second = render_registry_module(®istry).expect("second render should succeed");
assert_eq!(first, second);
assert!(first.contains("use std::env;"));
assert!(first.contains("pub enum Error {"));
assert!(first.contains("pub const VERSION: &str = \"1.0.0\";"));
assert!(first.contains("pub struct Server {"));
assert!(first.contains("pub fn all() -> Vec<Server> {"));
assert!(first.contains("pub fn get(id: &str) -> Option<Server> {"));
assert!(first.contains("pub fn version() -> &'static str {"));
assert!(first.contains("pub fn require(id: &str) -> Result<Server, Error> {"));
assert!(first.contains("pub fn agent_alpha_agent()"));
assert!(first.contains("pub fn agent_zeta_agent()"));
assert!(first.contains("Self::Npx => &[\"--yes\"]"));
assert!(first.contains("repository: None,"));
assert!(first.find("pub fn agent_alpha_agent()") < first.find("pub fn agent_beta_agent()"));
assert!(first.find("PackageManager::Npx") < first.find("PackageManager::Uvx"));
}
}