1use crate::{
2 types::{are_semver_compatible, SubtypeChecker},
3 CompositionGraph, PackageId,
4};
5use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum PlugError {
10 #[error("the socket component had no matching imports for the plugs that were provided")]
12 NoPlugHappened,
13 #[error("an error occurred when building the composition graph")]
15 GraphError {
16 #[source]
18 source: anyhow::Error,
19 },
20}
21
22pub fn plug(
27 graph: &mut CompositionGraph,
28 plugs: Vec<PackageId>,
29 socket: PackageId,
30) -> Result<(), PlugError> {
31 let socket_instantiation = graph.instantiate(socket);
32
33 for plug in plugs {
34 let mut plug_exports: Vec<(String, String)> = Vec::new();
37 let mut cache = Default::default();
38 let mut checker = SubtypeChecker::new(&mut cache);
39 for (name, plug_ty) in &graph.types()[graph[plug].ty()].exports {
40 let matching_import = graph.types()[graph[socket].ty()]
42 .imports
43 .get(name)
44 .map(|ty| (name.clone(), ty))
45 .or_else(|| {
46 graph.types()[graph[socket].ty()]
47 .imports
48 .iter()
49 .find(|(import_name, _)| are_semver_compatible(name, import_name))
50 .map(|(import_name, ty)| (import_name.clone(), ty))
51 });
52
53 if let Some((socket_name, socket_ty)) = matching_import {
54 if checker
55 .is_subtype(*plug_ty, graph.types(), *socket_ty, graph.types())
56 .is_ok()
57 {
58 plug_exports.push((name.clone(), socket_name));
59 }
60 }
61 }
62
63 let mut plug_instantiation = None;
65 for (plug_name, socket_name) in plug_exports {
66 log::debug!("using export `{plug_name}` for plug");
67 let plug_instantiation =
68 *plug_instantiation.get_or_insert_with(|| graph.instantiate(plug));
69 let export = graph
70 .alias_instance_export(plug_instantiation, &plug_name)
71 .map_err(|err| PlugError::GraphError { source: err.into() })?;
72 graph
73 .set_instantiation_argument(socket_instantiation, &socket_name, export)
74 .map_err(|err| PlugError::GraphError { source: err.into() })?;
75 }
76 }
77
78 if graph
80 .get_instantiation_arguments(socket_instantiation)
81 .next()
82 .is_none()
83 {
84 return Err(PlugError::NoPlugHappened);
85 }
86
87 for name in graph.types()[graph[socket].ty()]
89 .exports
90 .keys()
91 .cloned()
92 .collect::<Vec<_>>()
93 {
94 let export = graph
95 .alias_instance_export(socket_instantiation, &name)
96 .map_err(|err| PlugError::GraphError { source: err.into() })?;
97
98 graph
99 .export(export, &name)
100 .map_err(|err| PlugError::GraphError { source: err.into() })?;
101 }
102
103 Ok(())
104}