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}