wac_resolver/
lib.rs

1//! Modules for package resolvers.
2
3use indexmap::IndexMap;
4use miette::{Diagnostic, SourceSpan};
5use wac_parser::Document;
6
7mod fs;
8#[cfg(feature = "registry")]
9mod registry;
10mod visitor;
11
12pub use fs::*;
13#[cfg(feature = "registry")]
14pub use registry::*;
15pub use visitor::*;
16use wac_types::BorrowedPackageKey;
17
18/// Represents a package resolution error.
19#[derive(thiserror::Error, Diagnostic, Debug)]
20#[diagnostic(code("failed to resolve document"))]
21pub enum Error {
22    /// Failed to create registry client.
23    #[error("failed to create registry client: {0:#}")]
24    RegistryClientFailed(anyhow::Error),
25    /// An unknown package was encountered.
26    #[error("unknown package `{name}`")]
27    UnknownPackage {
28        /// The name of the package.
29        name: String,
30        /// The span where the error occurred.
31        #[label(primary, "unknown package `{name}`")]
32        span: SourceSpan,
33    },
34    /// An invalid package name was encountered.
35    #[error("invalid package name `{name}`")]
36    InvalidPackageName {
37        /// The name of the package.
38        name: String,
39        /// The span where the error occurred.
40        #[label(primary, "invalid package name `{name}`")]
41        span: SourceSpan,
42    },
43    /// An unknown package version was encountered.
44    #[cfg(feature = "registry")]
45    #[error("version {version} of package `{name}` does not exist")]
46    UnknownPackageVersion {
47        /// The name of the package.
48        name: String,
49        /// The version of the package that does not exist.
50        version: semver::Version,
51        /// The span where the error occurred.
52        #[label(primary, "unknown package version `{version}`")]
53        span: SourceSpan,
54    },
55    /// Cannot instantiate the package being defined.
56    #[error("cannot instantiate the package being defined")]
57    CannotInstantiateSelf {
58        /// The span where the error occurred.
59        #[label(primary, "cannot instantiate self")]
60        span: SourceSpan,
61    },
62    /// Cannot instantiate the package being defined.
63    #[error("package `{name}` does not exist in the registry")]
64    PackageDoesNotExist {
65        /// The name of the package that does not exist.
66        name: String,
67        /// The span where the error occurred.
68        #[label(primary, "package `{name}` does not exist")]
69        span: SourceSpan,
70    },
71    /// The requested package version does not exist.
72    #[cfg(feature = "registry")]
73    #[error("version {version} of package `{name}` does not exist")]
74    PackageVersionDoesNotExist {
75        /// The name of the package.
76        name: String,
77        /// The version of the package.
78        version: semver::Version,
79        /// The span where the error occurred.
80        #[label(primary, "{version} does not exist")]
81        span: SourceSpan,
82    },
83    /// A package has no releases.
84    #[cfg(feature = "registry")]
85    #[error("package `{name}` has no releases")]
86    PackageNoReleases {
87        /// The name of the package.
88        name: String,
89        /// The span where the error occurred.
90        #[label(primary, "package `{name}` has no releases")]
91        span: SourceSpan,
92    },
93    /// A failure occurred while updating logs from the registry.
94    #[cfg(feature = "registry")]
95    #[error("failed to update registry logs")]
96    RegistryUpdateFailure {
97        /// The underlying error.
98        #[source]
99        source: anyhow::Error,
100    },
101    /// A failure occurred while downloading content from the registry.
102    #[cfg(feature = "registry")]
103    #[error("failed to download content from the registry")]
104    RegistryDownloadFailure {
105        /// The underlying error.
106        #[source]
107        source: anyhow::Error,
108    },
109    /// A failure occurred while reading content from registry storage.
110    #[cfg(feature = "registry")]
111    #[error("failed to read content path `{path}`", path = .path.display())]
112    RegistryContentFailure {
113        /// The path to the content.
114        path: std::path::PathBuf,
115        /// The underlying error.
116        #[source]
117        source: anyhow::Error,
118    },
119    /// A package failed to resolve.
120    #[error("failed to resolve package `{name}`")]
121    PackageResolutionFailure {
122        /// The name of the package.
123        name: String,
124        /// The span where the error occurred.
125        #[label(primary, "package `{name}` failed to resolve")]
126        span: SourceSpan,
127        /// The underlying error.
128        #[source]
129        source: anyhow::Error,
130    },
131}
132
133/// Builds a map of packages referenced in a document to the first span
134/// which references the package.
135pub fn packages<'a>(
136    document: &'a Document<'a>,
137) -> Result<IndexMap<BorrowedPackageKey<'a>, SourceSpan>, Error> {
138    let mut keys = IndexMap::new();
139    let mut visitor = PackageVisitor::new(|name, version, span| {
140        if name == document.directive.package.name {
141            return true;
142        }
143
144        if keys
145            .insert(
146                BorrowedPackageKey::from_name_and_version(name, version),
147                span,
148            )
149            .is_none()
150        {
151            if let Some(version) = version {
152                log::debug!("discovered reference to package `{name}` ({version})");
153            } else {
154                log::debug!("discovered reference to package `{name}`");
155            }
156        }
157
158        true
159    });
160
161    visitor.visit(document)?;
162    Ok(keys)
163}