Skip to content

Commit 4c85b76

Browse files
committed
add registry support to plug subcommand
1 parent 88099dc commit 4c85b76

3 files changed

Lines changed: 106 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ wac-types = { workspace = true }
2323
wac-graph = { workspace = true }
2424
wac-resolver = { workspace = true, default-features = false }
2525
wac-parser = { workspace = true, default-features = false }
26+
warg-client = { workspace = true, optional = true }
27+
warg-protocol = { workspace = true, optional = true }
2628
anyhow = { workspace = true }
2729
clap = { workspace = true }
2830
pretty_env_logger = { workspace = true }
@@ -43,7 +45,7 @@ indicatif = { workspace = true, optional = true }
4345
default = ["wit", "registry"]
4446
wat = ["wac-resolver/wat"]
4547
wit = ["wac-resolver/wit"]
46-
registry = ["wac-resolver/registry", "dep:indicatif"]
48+
registry = ["wac-resolver/registry", "dep:indicatif", "dep:warg-client", "dep:warg-protocol"]
4749

4850
[workspace.dependencies]
4951
wac-parser = { path = "crates/wac-parser", version = "0.1.0", default-features = false }

src/commands/plug.rs

Lines changed: 101 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,69 @@ use std::{
33
path::PathBuf,
44
};
55

6-
use anyhow::{bail, Context as _, Result};
6+
use anyhow::{anyhow, bail, Context as _, Error, Result};
77
use clap::Args;
8+
use std::borrow::Cow;
9+
use std::str::FromStr;
810
use wac_graph::{CompositionGraph, EncodeOptions, NodeId, PackageId};
911
use wac_types::{Package, SubtypeChecker};
1012

13+
#[cfg(feature = "registry")]
14+
use warg_client::FileSystemClient;
15+
16+
#[cfg(feature = "registry")]
17+
use warg_protocol::registry::PackageName;
18+
19+
/// The package path or registry package name.
20+
#[derive(Clone, Debug)]
21+
pub enum PackageRef {
22+
/// The local file path to the component.
23+
LocalPath(PathBuf),
24+
/// The registry package name.
25+
#[cfg(feature = "registry")]
26+
RegistryPackage(PackageName), // TODO handle package versions
27+
}
28+
29+
impl FromStr for PackageRef {
30+
type Err = Error;
31+
32+
fn from_str(s: &str) -> Result<Self, Self::Err> {
33+
if cfg!(not(feature = "registry")) {
34+
return Ok(Self::LocalPath(PathBuf::from(s)));
35+
}
36+
37+
#[cfg(feature = "registry")]
38+
if let Ok(package_name) = PackageName::new(s) {
39+
// only `namespace:package-name` without file extensions is valid
40+
Ok(Self::RegistryPackage(package_name))
41+
} else {
42+
Ok(Self::LocalPath(PathBuf::from(s)))
43+
}
44+
}
45+
}
46+
47+
impl std::fmt::Display for PackageRef {
48+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49+
match self {
50+
Self::LocalPath(path) => write!(f, "{}", path.display()),
51+
Self::RegistryPackage(name) => write!(f, "{}", name),
52+
}
53+
}
54+
}
55+
1156
/// Plugs the exports of any number of 'plug' components into the imports of a 'socket' component.
1257
#[derive(Args)]
1358
#[clap(disable_version_flag = true)]
1459
pub struct PlugCommand {
15-
/// The path to the plug component.
60+
/// The local path to the plug component or the registry package name.
1661
///
1762
/// More than one plug can be supplied.
1863
#[clap(long = "plug", value_name = "PLUG_PATH", required = true)]
19-
pub plugs: Vec<PathBuf>,
64+
pub plugs: Vec<PackageRef>,
2065

21-
/// The path to the socket component
66+
/// The local path to the socket component or the registry package name.
2267
#[clap(value_name = "SOCKET_PATH", required = true)]
23-
pub socket: PathBuf,
68+
pub socket: PackageRef,
2469

2570
/// Whether to emit the WebAssembly text format.
2671
#[clap(long, short = 't')]
@@ -31,6 +76,11 @@ pub struct PlugCommand {
3176
/// If not specified, the output will be written to stdout.
3277
#[clap(long, short = 'o')]
3378
pub output: Option<PathBuf>,
79+
80+
/// The URL of the registry to use.
81+
#[cfg(feature = "registry")]
82+
#[clap(long, value_name = "URL")]
83+
pub registry: Option<String>,
3484
}
3585

3686
impl PlugCommand {
@@ -39,10 +89,28 @@ impl PlugCommand {
3989
log::debug!("executing plug command");
4090
let mut graph = CompositionGraph::new();
4191

42-
let socket = std::fs::read(&self.socket).with_context(|| {
92+
#[cfg(feature = "registry")]
93+
let client = FileSystemClient::new_with_default_config(self.registry.as_deref()).ok();
94+
95+
let socket_path = match &self.socket {
96+
#[cfg(feature = "registry")]
97+
PackageRef::RegistryPackage(name) => {
98+
client
99+
.as_ref()
100+
.ok_or_else(|| {
101+
anyhow!("Warg registry is not configured. Package `{name}` was not found.")
102+
})?
103+
.download(name, &semver::VersionReq::STAR)
104+
.await?
105+
.ok_or_else(|| anyhow!("package `{name}` was not found"))?
106+
.path
107+
}
108+
PackageRef::LocalPath(path) => path.clone(),
109+
};
110+
let socket = std::fs::read(&socket_path).with_context(|| {
43111
format!(
44-
"failed to read socket component `{path}`",
45-
path = self.socket.display()
112+
"failed to read socket component `{socket}`",
113+
socket = self.socket
46114
)
47115
})?;
48116

@@ -53,24 +121,40 @@ impl PlugCommand {
53121
// Collect the plugs by their names
54122
let mut plugs_by_name = std::collections::HashMap::<_, Vec<_>>::new();
55123
for plug in self.plugs.iter() {
56-
let name = plug
57-
.file_stem()
58-
.map(|fs| fs.to_string_lossy())
59-
.with_context(|| format!("path to plug '{}' was not a file", plug.display()))?;
124+
let name = match plug {
125+
PackageRef::RegistryPackage(name) => Cow::Borrowed(name.as_ref()),
126+
PackageRef::LocalPath(path) => path
127+
.file_stem()
128+
.map(|fs| fs.to_string_lossy())
129+
.with_context(|| format!("path to plug '{}' was not a file", plug))?,
130+
};
131+
60132
// TODO(rylev): sanitize the name to ensure it's a valid package identifier.
61133
plugs_by_name.entry(name).or_default().push(plug);
62134
}
63135

64136
// Plug each plug into the socket.
65-
for (name, plug_paths) in plugs_by_name {
66-
for (i, plug_path) in plug_paths.iter().enumerate() {
67-
let mut name = format!("plug:{name}");
137+
for (name, plug_refs) in plugs_by_name {
138+
for (i, plug_ref) in plug_refs.iter().enumerate() {
139+
let (mut name, path) = match plug_ref {
140+
PackageRef::RegistryPackage(name) => (
141+
name.as_ref().to_string(),
142+
client
143+
.as_ref()
144+
.ok_or_else(|| anyhow!("Warg registry is not configured. Package `{name}` was not found."))?
145+
.download(name, &semver::VersionReq::STAR)
146+
.await?
147+
.ok_or_else(|| anyhow!("package `{name}` was not found"))?
148+
.path,
149+
),
150+
PackageRef::LocalPath(path) => (format!("plug:{name}"), path.clone()),
151+
};
68152
// If there's more than one plug with the same name, append an index to the name.
69-
if plug_paths.len() > 1 {
153+
if plug_refs.len() > 1 {
70154
use core::fmt::Write;
71155
write!(&mut name, "{i}").unwrap();
72156
}
73-
plug_into_socket(&name, plug_path, socket, socket_instantiation, &mut graph)?;
157+
plug_into_socket(&name, &path, socket, socket_instantiation, &mut graph)?;
74158
}
75159
}
76160

0 commit comments

Comments
 (0)