Skip to main content

cargo_uv/packages/
package.rs

1use crate::{
2    Action, Bumpable, CargoFile, PackageName, ReadToml, Result, VersionLocation, current_span,
3    manifest::version_location::VersionType,
4};
5use miette::bail;
6use semver::{BuildMetadata, Prerelease, Version};
7use std::path::{Path, PathBuf};
8use tracing::instrument;
9
10#[derive(Debug, Eq, Clone)]
11pub struct Package<CargoFileState> {
12    name: PackageName,
13    version_type: VersionType,
14    version: Version,
15    manifest_path: PathBuf,
16    cargo_file: CargoFile<CargoFileState>,
17}
18
19impl<CargoFileState: PartialEq> PartialEq for Package<CargoFileState> {
20    fn eq(&self, other: &Self) -> bool {
21        self.name == other.name && self.manifest_path == other.manifest_path
22    }
23}
24
25impl<CargoFileState: std::hash::Hash> std::hash::Hash for Package<CargoFileState> {
26    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
27        self.name.hash(state);
28        // self.version_type.hash(state);
29        // self.manifest_path.hash(state);
30        // self.cargo_file.hash(state);
31    }
32}
33
34impl<CargoFileState> Package<CargoFileState> {
35    pub fn name(&self) -> &PackageName {
36        &self.name
37    }
38
39    pub fn version(&self) -> &Version {
40        &self.version
41    }
42
43    pub fn version_mut(&mut self) -> &mut Version {
44        &mut self.version
45    }
46
47    pub fn manifest_path(&self) -> &PathBuf {
48        &self.manifest_path
49    }
50
51    pub fn cargo_file(&self) -> &CargoFile<CargoFileState> {
52        &self.cargo_file
53    }
54
55    pub fn cargo_file_mut(&mut self) -> &mut CargoFile<CargoFileState> {
56        &mut self.cargo_file
57    }
58
59    pub fn version_type(&self) -> VersionType {
60        self.version_type
61    }
62}
63
64impl From<cargo_metadata::Package> for Package<ReadToml> {
65    fn from(meta_package: cargo_metadata::Package) -> Package<ReadToml> {
66        let manifest_path: PathBuf = meta_package.manifest_path.into();
67        let cargo_file = CargoFile::new(manifest_path.clone()).expect("from cargo manifest");
68        Self {
69            name: meta_package.name.to_string().into(),
70            version: meta_package.version,
71            version_type: Package::set_version_type(&cargo_file)
72                .expect("Cargo manifest run with no error"),
73            cargo_file: cargo_file,
74            manifest_path,
75        }
76    }
77}
78impl Package<ReadToml> {
79    #[instrument(skip_all)]
80    pub fn set_version_type(cargo_file: &CargoFile<ReadToml>) -> Result<VersionType> {
81        let package = match VersionLocation::Package.get_version(cargo_file) {
82            Ok(_v) => Ok(VersionType::Package),
83            Err(e) => match e.kind() {
84                crate::VersionLocationErrorKind::SetByWorkspace => Ok(VersionType::SetByWorkspace),
85                _ => Err(e),
86            },
87        };
88
89        if let Ok(ver_type) = package {
90            return Ok(ver_type);
91        }
92
93        let ws = match VersionLocation::WorkspacePackage.get_version(cargo_file) {
94            Ok(_v) => VersionType::WorkspacePackage,
95            Err(e) => {
96                tracing::error!("Invalid version: {}", cargo_file.path().display());
97                return Err(miette::miette!(e)
98                    .wrap_err(package.map_err(|e| e.to_string()).err().unwrap_or_default()));
99            }
100        };
101        Ok(ws)
102    }
103
104    pub fn set_version(&mut self, version: Version) -> Result<Version> {
105        self.version = version.clone();
106        let cargo_file = self.cargo_file_mut();
107        let res = cargo_file.set_version(version);
108        res?;
109        Ok(self.version().clone())
110    }
111
112    #[instrument(skip(self, pre, build), fields(from, to))]
113    pub fn bump_version(
114        &mut self,
115        action: Action,
116        pre: Option<Prerelease>,
117        build: Option<BuildMetadata>,
118        force: bool,
119    ) -> Result<Version> {
120        let span = current_span!();
121        span.record("from", self.version.to_string());
122        let name = self.name().clone();
123        tracing::trace!("Package {}: Bump Version", name);
124
125        let version = self.version_mut();
126        let new_version = version.bump(action, pre, build, force)?;
127        self.cargo_file_mut().set_version(new_version)?;
128        span.record("to", self.version().to_string());
129        println!("{name}: {}", self.version());
130        Ok(self.version().clone())
131    }
132
133    pub fn write_cargo_file(&mut self) -> Result<Version> {
134        if self.version_type() == VersionType::SetByWorkspace {
135            let msg = format!(
136                "Can't modify SetByWorkspace version from a bool: {}",
137                self.manifest_path.as_os_str().display()
138            );
139            tracing::error!("{}", msg);
140            bail!("{msg}")
141        }
142        self.cargo_file_mut().write_cargo_file()?;
143        match self.version_type() {
144            VersionType::Package => Ok(VersionLocation::Package.get_version(self.cargo_file())?),
145            VersionType::SetByWorkspace => unreachable!(),
146
147            VersionType::WorkspacePackage => {
148                Ok(VersionLocation::WorkspacePackage.get_version(self.cargo_file())?)
149            }
150        }
151    }
152
153    #[track_caller]
154    pub fn workspace_package(manifest_path: &Path) -> Result<Package<ReadToml>> {
155        let cargo_file = CargoFile::new(manifest_path)?;
156        let version = VersionLocation::WorkspacePackage.get_version(&cargo_file)?;
157
158        Ok(Package {
159            name: PackageName("workspace.package".into()),
160            version_type: VersionType::WorkspacePackage,
161            version,
162            manifest_path: manifest_path.into(),
163            cargo_file,
164        })
165    }
166}