cargo/ops/cargo_add/
crate_spec.rs

1//! Crate name parsing.
2
3use anyhow::Context as _;
4
5use super::Dependency;
6use crate::util::toml_mut::dependency::RegistrySource;
7use crate::CargoResult;
8use cargo_util_schemas::manifest::PackageName;
9
10/// User-specified crate
11///
12/// This can be a
13/// - Name (e.g. `docopt`)
14/// - Name and a version req (e.g. `docopt@^0.8`)
15#[derive(Debug)]
16pub struct CrateSpec {
17    /// Crate name
18    name: String,
19    /// Optional version requirement
20    version_req: Option<String>,
21}
22
23impl CrateSpec {
24    /// Convert a string to a `Crate`
25    pub fn resolve(pkg_id: &str) -> CargoResult<Self> {
26        let (name, version) = pkg_id
27            .split_once('@')
28            .map(|(n, v)| (n, Some(v)))
29            .unwrap_or((pkg_id, None));
30
31        PackageName::new(name)?;
32
33        if let Some(version) = version {
34            semver::VersionReq::parse(version)
35                .with_context(|| format!("invalid version requirement `{version}`"))?;
36        }
37
38        let id = Self {
39            name: name.to_owned(),
40            version_req: version.map(|s| s.to_owned()),
41        };
42
43        Ok(id)
44    }
45
46    /// Generate a dependency entry for this crate specifier
47    pub fn to_dependency(&self) -> CargoResult<Dependency> {
48        let mut dep = Dependency::new(self.name());
49        if let Some(version_req) = self.version_req() {
50            dep = dep.set_source(RegistrySource::new(version_req));
51        }
52        Ok(dep)
53    }
54
55    pub fn name(&self) -> &str {
56        &self.name
57    }
58
59    pub fn version_req(&self) -> Option<&str> {
60        self.version_req.as_deref()
61    }
62}