1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use std::fmt::{Debug, Display};

use crate::runtime::resolver::{PackageSpecifier, PackageSummary};

/// Something that packages can be downloaded from.
#[async_trait::async_trait]
pub trait Source: Sync + Debug {
    /// Ask this source which packages would satisfy a particular
    /// [`Dependency`][dep] constraint.
    ///
    /// # Assumptions
    ///
    /// If this method returns a successful result, it is guaranteed that there
    /// will be at least one [`PackageSummary`], otherwise implementations
    /// should return [`QueryError::NotFound`] or [`QueryError::NoMatches`].
    ///
    /// [dep]: crate::runtime::resolver::Dependency
    async fn query(&self, package: &PackageSpecifier) -> Result<Vec<PackageSummary>, QueryError>;

    /// Run [`Source::query()`] and get the [`PackageSummary`] for the latest
    /// version.
    async fn latest(&self, pkg: &PackageSpecifier) -> Result<PackageSummary, QueryError> {
        let candidates = self.query(pkg).await?;
        candidates
            .into_iter()
            .max_by(|left, right| left.pkg.version.cmp(&right.pkg.version))
            .ok_or(QueryError::NoMatches {
                archived_versions: Vec::new(),
            })
    }
}

#[async_trait::async_trait]
impl<D, S> Source for D
where
    D: std::ops::Deref<Target = S> + Debug + Send + Sync,
    S: Source + ?Sized + Send + Sync + 'static,
{
    async fn query(&self, package: &PackageSpecifier) -> Result<Vec<PackageSummary>, QueryError> {
        (**self).query(package).await
    }
}

#[derive(Debug)]
pub enum QueryError {
    Unsupported,
    NotFound,
    NoMatches {
        archived_versions: Vec<semver::Version>,
    },
    Other(anyhow::Error),
}

impl From<anyhow::Error> for QueryError {
    fn from(value: anyhow::Error) -> Self {
        QueryError::Other(value)
    }
}

impl Display for QueryError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            QueryError::Unsupported => {
                f.write_str("This type of package specifier isn't supported")
            }
            QueryError::NotFound => f.write_str("Not found"),
            QueryError::NoMatches { archived_versions } => match archived_versions.as_slice() {
                [] => f.write_str(
                    "The package was found, but no published versions matched the constraint",
                ),
                [version] => write!(
                    f,
                    "The only version satisfying the constraint, {version}, is archived"
                ),
                [first, rest @ ..] => {
                    let num_others = rest.len();
                    write!(
                        f,
                        "Unable to satisfy the request. Version {first}, and {num_others} are all archived"
                    )
                }
            },
            QueryError::Other(e) => Display::fmt(e, f),
        }
    }
}

impl std::error::Error for QueryError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            QueryError::Other(e) => Some(&**e),
            QueryError::Unsupported | QueryError::NotFound | QueryError::NoMatches { .. } => None,
        }
    }
}