wasmer_wasix/runtime/resolver/
source.rs

1use std::{
2    fmt::{Debug, Display},
3    sync::Arc,
4};
5
6use wasmer_config::package::{PackageIdent, PackageSource};
7
8use crate::runtime::resolver::PackageSummary;
9
10/// Something that packages can be downloaded from.
11#[async_trait::async_trait]
12pub trait Source: Sync + Debug {
13    /// Ask this source which packages would satisfy a particular
14    /// [`Dependency`][dep] constraint.
15    ///
16    /// # Assumptions
17    ///
18    /// If this method returns a successful result, it is guaranteed that there
19    /// will be at least one [`PackageSummary`], otherwise implementations
20    /// should return [`QueryError::NotFound`] or [`QueryError::NoMatches`].
21    ///
22    /// [dep]: crate::runtime::resolver::Dependency
23    async fn query(&self, package: &PackageSource) -> Result<Vec<PackageSummary>, QueryError>;
24
25    /// Run [`Source::query()`] and get the [`PackageSummary`] for the latest
26    /// version.
27    async fn latest(&self, pkg: &PackageSource) -> Result<PackageSummary, QueryError> {
28        let candidates = self.query(pkg).await?;
29
30        match pkg {
31            PackageSource::Ident(PackageIdent::Named(_)) => candidates
32                .into_iter()
33                .max_by(|left, right| {
34                    let left_version = left.pkg.id.as_named().map(|x| &x.version);
35                    let right_version = right.pkg.id.as_named().map(|x| &x.version);
36
37                    left_version.cmp(&right_version)
38                })
39                .ok_or(QueryError::NoMatches {
40                    query: pkg.clone(),
41                    archived_versions: Vec::new(),
42                }),
43            _ => candidates
44                .into_iter()
45                .next()
46                .ok_or_else(|| QueryError::NotFound { query: pkg.clone() }),
47        }
48    }
49}
50
51#[async_trait::async_trait]
52impl<D, S> Source for D
53where
54    D: std::ops::Deref<Target = S> + Debug + Send + Sync,
55    S: Source + ?Sized + Send + Sync + 'static,
56{
57    async fn query(&self, package: &PackageSource) -> Result<Vec<PackageSummary>, QueryError> {
58        (**self).query(package).await
59    }
60}
61
62#[derive(Clone, Debug)]
63pub enum QueryError {
64    Unsupported {
65        query: PackageSource,
66    },
67    NotFound {
68        query: PackageSource,
69    },
70    NoMatches {
71        query: PackageSource,
72        archived_versions: Vec<semver::Version>,
73    },
74    Timeout {
75        query: PackageSource,
76    },
77    Other {
78        query: PackageSource,
79        // Arc to make it cloneable
80        // Cloning is important for some use-cases.
81        error: Arc<anyhow::Error>,
82    },
83}
84
85impl QueryError {
86    pub fn query(&self) -> &PackageSource {
87        match self {
88            Self::Unsupported { query }
89            | Self::NotFound { query }
90            | Self::NoMatches { query, .. }
91            | Self::Timeout { query }
92            | Self::Other { query, .. } => query,
93        }
94    }
95
96    pub fn new_other(err: anyhow::Error, query: &PackageSource) -> Self {
97        Self::Other {
98            query: query.clone(),
99            error: Arc::new(err),
100        }
101    }
102}
103
104impl Display for QueryError {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        write!(f, "failed to query package '{}': ", self.query())?;
107
108        match self {
109            Self::Unsupported { .. } => f.write_str("unsupported package specifier"),
110            Self::NotFound { .. } => f.write_str("not found"),
111            Self::Timeout { .. } => f.write_str("timeout"),
112            Self::NoMatches {
113                query: _,
114                archived_versions,
115            } => match archived_versions.as_slice() {
116                [] => f.write_str(
117                    "the package was found, but no published versions matched the constraint",
118                ),
119                [version] => write!(
120                    f,
121                    "the only version satisfying the constraint, {version}, is archived"
122                ),
123                [first, rest @ ..] => {
124                    let num_others = rest.len();
125                    write!(
126                        f,
127                        "unable to satisfy the request - version {first}, and {num_others} are all archived"
128                    )
129                }
130            },
131            Self::Other { error: e, query: _ } => Display::fmt(e, f),
132        }
133    }
134}
135
136impl std::error::Error for QueryError {
137    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
138        match self {
139            Self::Other { error, query: _ } => Some(&***error),
140            Self::Unsupported { .. }
141            | Self::NotFound { .. }
142            | Self::NoMatches { .. }
143            | Self::Timeout { .. } => None,
144        }
145    }
146}