cargo_uv/manifest/
toml_file.rs1use std::{
2 marker::PhantomData,
3 panic::Location,
4 path::{Path, PathBuf},
5};
6
7use miette::{IntoDiagnostic, bail};
8use semver::Version;
9use toml_edit::DocumentMut;
10use tracing::instrument;
11
12use crate::{
13 VersionLocation,
14 manifest::error::{CargoFileError, CargoFileErrorKind, VersionlocationError},
15};
16
17#[derive(Debug, Clone, Hash, PartialEq, Eq)]
19pub struct ReadToml;
20
21#[derive(Debug)]
23pub struct UnreadToml;
24
25#[derive(Clone)]
26pub struct CargoFile<State> {
27 path: PathBuf,
28 contents: Option<DocumentMut>,
29 __state: PhantomData<State>,
30}
31
32impl<State: std::fmt::Debug> std::fmt::Debug for CargoFile<State> {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_struct("CargoFile")
35 .field("path", &self.path)
36 .field("__state", &self.__state)
37 .finish()
38 }
39}
40
41impl<State: std::hash::Hash> std::hash::Hash for CargoFile<State> {
42 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
43 self.path.hash(state);
44 self.__state.hash(state);
45 }
46}
47
48impl<State: Eq> Eq for CargoFile<State> {}
49
50impl<State: PartialEq> PartialEq for CargoFile<State> {
51 fn eq(&self, other: &Self) -> bool {
52 self.path == other.path && self.__state == other.__state
53 }
54}
55
56impl<'a, S> CargoFile<S> {
57 pub fn path(&'a self) -> &'a Path {
58 &self.path
59 }
60}
61
62impl CargoFile<UnreadToml> {
63 #[instrument]
64 #[track_caller]
65 pub fn new(path: impl Into<PathBuf> + std::fmt::Debug) -> miette::Result<CargoFile<ReadToml>> {
66 let ret: CargoFile<UnreadToml> = Self::new_lazy(path.into());
67 let ret: CargoFile<ReadToml> = ret.read_file()?;
68 tracing::trace!("New Cargo file: {}", ret.path().display());
69 Ok(ret)
70 }
71
72 pub fn new_lazy(path: impl Into<PathBuf>) -> CargoFile<UnreadToml> {
73 Self {
74 path: path.into(),
75 contents: None,
76 __state: PhantomData::<UnreadToml>,
77 }
78 }
79
80 #[instrument(skip(self), fields(self.path))]
81 #[track_caller]
82 pub fn read_file(self) -> miette::Result<CargoFile<ReadToml>> {
83 let CargoFile { path, .. } = self;
84 let contents = match ::std::fs::read_to_string(&path) {
85 Ok(contents) => contents,
86 Err(e) => {
87 let msg = format!("Failed to read to string: {} - {}", e, path.display());
88 tracing::error!(msg);
89 let loc = Location::caller().to_string();
90 tracing::error!(loc);
91 bail!(msg)
92 }
93 };
94
95 let contents = Some(contents.parse::<DocumentMut>().into_diagnostic()?);
96 Ok(CargoFile {
97 path,
98 contents,
99 __state: PhantomData::<ReadToml>,
100 })
101 }
102}
103
104impl CargoFile<ReadToml> {
105 pub fn contents(&self) -> Option<&DocumentMut> {
106 self.contents.as_ref()
107 }
108 pub fn contents_mut(&mut self) -> Option<&mut DocumentMut> {
109 self.contents.as_mut()
110 }
111 #[instrument(skip_all)]
112 pub fn get_package_version(&self) -> Option<Version> {
113 VersionLocation::Package.get_version(&self).ok()
114 }
115 #[instrument(skip_all)]
116 pub fn get_workspace_version(&self) -> Option<Version> {
117 VersionLocation::WorkspacePackage.get_version(&self).ok()
118 }
119
120 #[track_caller]
121 #[instrument(skip(self))]
122 pub fn set_package_version(
123 &mut self,
124 new_version: &Version,
125 ) -> miette::Result<(), CargoFileError> {
126 VersionLocation::Package
127 .set_version(self, new_version)
128 .map_err(|e: VersionlocationError| {
129 let path = (&e).path().to_path_buf();
130 CargoFileErrorKind::LocationError(e).to_error(path)
131 })
132 }
133
134 #[track_caller]
135 #[instrument(skip(self))]
136 pub fn set_workspace_version(
137 &mut self,
138 new_version: &Version,
139 ) -> miette::Result<(), CargoFileError> {
140 VersionLocation::WorkspacePackage
141 .set_version(self, new_version)
142 .map_err(|e: VersionlocationError| {
143 let path = (&e).path().to_path_buf();
144 CargoFileErrorKind::LocationError(e).to_error(path)
145 })
146 }
147
148 #[track_caller]
149 #[instrument(skip(self))]
150 pub fn set_version(&mut self, new_version: Version) -> miette::Result<(), CargoFileError> {
151 let cargo_path = self.path().to_path_buf();
152 let pack_err = VersionLocation::Package.set_version(self, &new_version);
153 let ws_err = VersionLocation::WorkspacePackage.set_version(self, &new_version);
154
155 if let Some(cargo_file_err) = pack_err.err() {
156 use crate::manifest::error::VersionLocationErrorKind as VerLocErrKind;
157 match &cargo_file_err.kind() {
158 VerLocErrKind::SetByWorkspace => (),
159 VerLocErrKind::PackageNotFound => (),
160 VerLocErrKind::NotFound(_) => (),
161 VerLocErrKind::WorkspaceNotFound => unreachable!("Setting package"),
162 VerLocErrKind::ItemInvalid(_) | VerLocErrKind::SemverError(_) => {
163 return Err(
164 CargoFileErrorKind::LocationError(cargo_file_err).to_error(cargo_path)
165 );
166 }
167 }
168 } else {
169 return Ok(());
170 };
171
172 if let Some(ver_loc_error) = ws_err.err() {
173 use crate::manifest::error::VersionLocationErrorKind as VerLocErrKind;
174 match ver_loc_error.kind() {
175 VerLocErrKind::SetByWorkspace => unreachable!(),
176 VerLocErrKind::NotFound(_) => {
177 Err(CargoFileErrorKind::NoPackageOrWorkspaceVersion.to_error(cargo_path))
178 }
179 VerLocErrKind::PackageNotFound => unreachable!(),
180 VerLocErrKind::WorkspaceNotFound => {
181 Err(CargoFileErrorKind::NoPackageOrWorkspaceVersion.to_error(cargo_path))
182 }
183 VerLocErrKind::ItemInvalid(_) | VerLocErrKind::SemverError(_) => {
184 Err(CargoFileErrorKind::LocationError(ver_loc_error).to_error(cargo_path))
185 }
186 }
187 } else {
188 Ok(())
189 }
190 }
191
192 #[instrument(skip(self))]
193 pub fn write_cargo_file(&mut self) -> miette::Result<()> {
194 let contents = self.contents.as_ref().unwrap().to_string();
195 std::fs::write(&self.path, contents).into_diagnostic()?;
196 Ok(())
197 }
198}