cargo_whatfeatures/
lib.rs

1/*! whatfeatures
2
3print out features and dependencies for a specific crate
4*/
5
6mod args;
7mod client;
8mod features;
9mod printer;
10mod registry;
11mod util;
12
13#[doc(inline)]
14pub use client::{Client, Version};
15
16// TODO remove this
17#[doc(hidden)]
18pub use client::json;
19
20#[doc(inline)]
21pub use registry::{Crate, Registry, YankState};
22
23#[doc(inline)]
24pub use args::{Args, PkgId};
25
26#[doc(inline)]
27pub use printer::*;
28
29// TODO move all of this to another module
30
31#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
32/// An error for when the crate is in 'offline mode'
33pub enum OfflineError {
34    /// Cannot list versions
35    List,
36    /// Cannot get the latest version
37    Latest,
38    /// Crate wasn't cached locally, cannot look it up
39    CacheMiss,
40}
41
42impl OfflineError {
43    /// Converts this type to an Error type
44    pub fn to_error(&self) -> anyhow::Error {
45        let err = match self {
46            Self::List => {
47                "must be able to connect to https://crates.io to list versions"
48            }
49            Self::Latest =>{
50                "cannot find that crate cached, you must be able to connect to https://crates.io to get the latest version"
51            } ,
52            Self::CacheMiss => {
53                "crate not found in local registry or cache. must be able to connect to https://crates.io to fetch it"
54            },
55        };
56        anyhow::anyhow!(err)
57    }
58}
59
60#[derive(Debug)]
61/// Lookup result
62pub enum Lookup {
63    /// A partial lookup -- this has to cache the crate
64    Partial(Version),
65    /// The latest version from the cache
66    LocalCache(features::Workspace),
67    /// A local workspace
68    Workspace(features::Workspace),
69}
70
71/// Find this 'pkgid'
72pub fn lookup(pkg_id: &PkgId, client: &Option<Client>, is_local: bool) -> anyhow::Result<Lookup> {
73    match pkg_id {
74        // lookup the latest version
75        PkgId::Remote { name, semver } => {
76            let client = match &client {
77                Some(client) => client,
78                None => {
79                    return Registry::from_local()?
80                        .maybe_latest(&name)
81                        .ok_or_else(|| OfflineError::Latest.to_error())?
82                        .get_features()
83                        .map(Lookup::LocalCache)
84                }
85            };
86
87            let pkg = match semver {
88                Some(semver) => client.get_version(name, semver),
89                None => client.get_latest(name),
90            }
91            .map_err(|_err| cannot_find(pkg_id))?;
92
93            Ok(Lookup::Partial(pkg))
94        }
95
96        // otherwise load it from the local path
97        PkgId::Local(path) if !is_local => Crate::from_path(path).map(Lookup::Workspace),
98        PkgId::Local(path) => Crate::from_local(path).map(Lookup::Workspace),
99    }
100}
101
102fn cannot_find(pkg_id: &PkgId) -> anyhow::Error {
103    anyhow::anyhow!(
104        "cannot find a crate matching '{}'. maybe it was yanked?",
105        pkg_id
106    )
107}