wac_cli/
lib.rs

1//! A library for encoding and decoding WebAssembly compositions.
2
3#![deny(missing_docs)]
4
5use anyhow::Result;
6use indexmap::IndexMap;
7use miette::{GraphicalReportHandler, GraphicalTheme, NamedSource, Report};
8use std::{
9    collections::HashMap,
10    io::IsTerminal,
11    path::{Path, PathBuf},
12};
13use wac_parser::Document;
14use wac_resolver::{packages, Error, FileSystemPackageResolver};
15use wac_types::BorrowedPackageKey;
16
17pub mod commands;
18
19#[cfg(feature = "registry")]
20mod progress;
21
22fn fmt_err(e: impl Into<Report>, path: &Path, source: &str) -> anyhow::Error {
23    let mut s = String::new();
24    let e = e.into();
25    GraphicalReportHandler::new()
26        .with_cause_chain()
27        .with_theme(if std::io::stderr().is_terminal() {
28            GraphicalTheme::unicode()
29        } else {
30            GraphicalTheme::unicode_nocolor()
31        })
32        .render_report(
33            &mut s,
34            e.with_source_code(NamedSource::new(path.to_string_lossy(), source.to_string()))
35                .as_ref(),
36        )
37        .expect("failed to render diagnostic");
38    anyhow::Error::msg(s)
39}
40
41/// Represents a package resolver used to resolve packages
42/// referenced from a document.
43///
44/// The resolver first checks the file system for a matching package.
45///
46/// If it cannot find a matching package, it will check the registry.
47pub struct PackageResolver {
48    fs: FileSystemPackageResolver,
49    #[cfg(feature = "registry")]
50    registry: Option<wac_resolver::RegistryPackageResolver>,
51}
52
53impl PackageResolver {
54    /// Creates a new package resolver.
55    pub async fn new(
56        dir: impl Into<PathBuf>,
57        overrides: HashMap<String, PathBuf>,
58        #[cfg(feature = "registry")] registry: Option<&str>,
59    ) -> Result<Self> {
60        Ok(Self {
61            fs: FileSystemPackageResolver::new(dir, overrides, false),
62            #[cfg(feature = "registry")]
63            registry: if registry.is_some() {
64                Some(
65                    wac_resolver::RegistryPackageResolver::new(
66                        registry,
67                        Some(Box::new(progress::ProgressBar::new())),
68                    )
69                    .await?,
70                )
71            } else {
72                None
73            },
74        })
75    }
76
77    /// Resolve all packages referenced in the given document.
78    pub async fn resolve<'a>(
79        &mut self,
80        document: &'a Document<'a>,
81    ) -> Result<IndexMap<BorrowedPackageKey<'a>, Vec<u8>>, Error> {
82        let mut keys = packages(document)?;
83
84        // Next, we resolve as many of the packages from the file system as possible
85        // and filter out the ones that were resolved.
86        #[allow(unused_mut)]
87        let mut packages = self.fs.resolve(&keys)?;
88        keys.retain(|key, _| !packages.contains_key(key));
89
90        // Next resolve the remaining packages from the registry
91        // The registry resolver will error on missing package
92        #[cfg(feature = "registry")]
93        if !keys.is_empty() {
94            if self.registry.is_none() {
95                self.registry = Some(
96                    wac_resolver::RegistryPackageResolver::new(
97                        None,
98                        Some(Box::new(progress::ProgressBar::new())),
99                    )
100                    .await
101                    .map_err(Error::RegistryClientFailed)?,
102                );
103            }
104            let reg_packages = self.registry.as_ref().unwrap().resolve(&keys).await?;
105            keys.retain(|key, _| !reg_packages.contains_key(key));
106            packages.extend(reg_packages);
107        }
108
109        // At this point keys should be empty, otherwise we have an unknown package
110        if let Some((key, span)) = keys.first() {
111            return Err(Error::UnknownPackage {
112                name: key.name.to_string(),
113                span: *span,
114            });
115        }
116
117        Ok(packages)
118    }
119}