Skip to content

Commit 557a430

Browse files
committed
Introduce a new command 'plug'
The goal of the command is to treat one component as a plug which will have all of its exports that match the imports of another component, the socket, plugged into the socket component. The exports of the socket will be re-exported.
1 parent 75478f2 commit 557a430

3 files changed

Lines changed: 100 additions & 1 deletion

File tree

src/bin/wac.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Result;
22
use clap::Parser;
33
use owo_colors::{OwoColorize, Stream, Style};
4-
use wac_cli::commands::{EncodeCommand, ParseCommand, ResolveCommand};
4+
use wac_cli::commands::{EncodeCommand, ParseCommand, PlugCommand, ResolveCommand};
55

66
fn version() -> &'static str {
77
option_env!("CARGO_VERSION_INFO").unwrap_or(env!("CARGO_PKG_VERSION"))
@@ -20,6 +20,7 @@ enum Wac {
2020
Parse(ParseCommand),
2121
Resolve(ResolveCommand),
2222
Encode(EncodeCommand),
23+
Plug(PlugCommand),
2324
}
2425

2526
#[tokio::main]
@@ -30,6 +31,7 @@ async fn main() -> Result<()> {
3031
Wac::Parse(cmd) => cmd.exec().await,
3132
Wac::Resolve(cmd) => cmd.exec().await,
3233
Wac::Encode(cmd) => cmd.exec().await,
34+
Wac::Plug(cmd) => cmd.exec().await,
3335
} {
3436
eprintln!(
3537
"{error}: {e:?}",

src/commands.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
33
mod encode;
44
mod parse;
5+
mod plug;
56
mod resolve;
67

78
pub use self::encode::*;
89
pub use self::parse::*;
10+
pub use self::plug::*;
911
pub use self::resolve::*;

src/commands/plug.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::{io::Write as _, path::PathBuf};
2+
3+
use anyhow::{Context as _, Result};
4+
use clap::Args;
5+
use wac_graph::{CompositionGraph, EncodeOptions};
6+
use wac_types::{Package, SubtypeChecker};
7+
8+
/// Plugs the exports of any number of 'plug' components into the imports of a 'socket' component.
9+
#[derive(Args)]
10+
#[clap(disable_version_flag = true)]
11+
pub struct PlugCommand {
12+
/// The path to the plug component
13+
pub plug: PathBuf,
14+
15+
/// The path to the socket component
16+
pub socket: PathBuf,
17+
18+
/// The path to write the output to.
19+
///
20+
/// If not specified, the output will be written to stdout.
21+
#[clap(long, short = 'o')]
22+
pub output: Option<PathBuf>,
23+
}
24+
25+
impl PlugCommand {
26+
/// Executes the command.
27+
pub async fn exec(&self) -> Result<()> {
28+
log::debug!("executing plug command");
29+
let mut graph = CompositionGraph::new();
30+
31+
let plug = std::fs::read(&self.plug).with_context(|| {
32+
format!(
33+
"failed to read plug component `{path}`",
34+
path = self.plug.display()
35+
)
36+
})?;
37+
let socket = std::fs::read(&self.socket).with_context(|| {
38+
format!(
39+
"failed to read socket component `{path}`",
40+
path = self.plug.display()
41+
)
42+
})?;
43+
44+
// Register the packages
45+
let plug = Package::from_bytes("plug", None, plug, graph.types_mut())?;
46+
let plug = graph.register_package(plug)?;
47+
let socket = Package::from_bytes("socket", None, socket, graph.types_mut())?;
48+
let socket = graph.register_package(socket)?;
49+
50+
let socket_instantiation = graph.instantiate(socket);
51+
let plug_instantiation = graph.instantiate(plug);
52+
let mut plugs = Vec::new();
53+
let mut cache = Default::default();
54+
let mut checker = SubtypeChecker::new(&mut cache);
55+
for (name, plug_ty) in &graph.types()[graph[plug].ty()].exports {
56+
if let Some(socket_ty) = graph.types()[graph[socket].ty()].imports.get(name) {
57+
if let Ok(_) =
58+
checker.is_subtype(*plug_ty, graph.types(), *socket_ty, graph.types())
59+
{
60+
plugs.push(name.clone());
61+
}
62+
}
63+
}
64+
for plug in plugs {
65+
log::debug!("using export `{plug}` for plug");
66+
let export = graph.alias_instance_export(plug_instantiation, &plug)?;
67+
graph.set_instantiation_argument(socket_instantiation, &plug, export)?;
68+
}
69+
for name in graph.types()[graph[socket].ty()]
70+
.exports
71+
.keys()
72+
.cloned()
73+
.collect::<Vec<_>>()
74+
{
75+
let export = graph.alias_instance_export(socket_instantiation, &name)?;
76+
graph.export(export, &name)?;
77+
}
78+
79+
let bytes = graph.encode(EncodeOptions::default())?;
80+
match &self.output {
81+
Some(path) => {
82+
std::fs::write(&path, bytes).context(format!(
83+
"failed to write output file `{path}`",
84+
path = path.display()
85+
))?;
86+
}
87+
None => {
88+
std::io::stdout()
89+
.write_all(&bytes)
90+
.context("failed to write to stdout")?;
91+
}
92+
}
93+
Ok(())
94+
}
95+
}

0 commit comments

Comments
 (0)