Skip to main content

cargo_uv/manifest/
version_location.rs

1use std::fmt::Display;
2
3use semver::Version;
4use tracing::{info, instrument, trace};
5
6use crate::{
7    CargoFile, ReadToml, current_span,
8    manifest::error::{ItemType, VersionlocationError},
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum VersionType {
13    Package,
14    SetByWorkspace,
15    WorkspacePackage,
16}
17
18#[derive(Debug, Clone, Copy)]
19pub enum VersionLocation {
20    Package,
21    WorkspacePackage,
22}
23
24impl Display for VersionLocation {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        write!(
27            f,
28            "{}",
29            match self {
30                VersionLocation::Package => "package.version",
31                VersionLocation::WorkspacePackage => "workspace.package.version",
32            }
33        )
34    }
35}
36
37impl VersionLocation {
38    #[track_caller]
39    #[instrument(skip_all, fields(version, path))]
40    pub fn get_version(
41        &self,
42        cargo_toml: &CargoFile<ReadToml>,
43    ) -> Result<Version, VersionlocationError> {
44        use crate::manifest::error::VersionLocationErrorKind as ErrKind;
45        let path = cargo_toml.path();
46
47        let set_err = |kind: ErrKind, context: Option<&'static str>| kind.to_error(path, context);
48        let _span = current_span!();
49        let document = cargo_toml
50            .contents()
51            .expect("Can't call this function without the document read.");
52        tracing::trace!("have document");
53        let ret = match self {
54            VersionLocation::Package => {
55                let package = document
56                    .get("package")
57                    .ok_or(ErrKind::PackageNotFound.to_error(path, Some("First check")))?;
58
59                let package_table = package.as_table().ok_or_else(|| {
60                    set_err(
61                        ErrKind::ItemInvalid(package.into()),
62                        Some("Tried to get package table"),
63                    )
64                })?;
65
66                match package_table.get("version").ok_or(set_err(
67                    ErrKind::NotFound(*self),
68                    Some("Package table located."),
69                ))? {
70                    toml_edit::Item::Value(value) => {
71                        Version::parse(value.as_str().unwrap()).map_err(|e| set_err(e.into(), None))
72                    }
73                    toml_edit::Item::Table(table) => {
74                        let workspace_table = table.get("workspace").ok_or(set_err(
75                            ErrKind::ItemInvalid(ItemType::Table),
76                            Some("expected version.workspace = <bool>"),
77                        ))?;
78                        let val = workspace_table.as_bool().ok_or(set_err(
79                            ErrKind::ItemInvalid(ItemType::Value),
80                            Some("Expected bool"),
81                        ))?;
82
83                        let msg = format!("in the manifest file: version.workspace = {val}");
84                        Err(ErrKind::SetByWorkspace.to_error(path, Some(msg)))
85                    }
86
87                    item => Err(set_err(ErrKind::ItemInvalid(item.into()), None)),
88                }
89            }
90            VersionLocation::WorkspacePackage => {
91                let workspace =
92                    document
93                        .get("workspace")
94                        .ok_or(ErrKind::WorkspaceNotFound.to_error(
95                            path,
96                            Some(format!(
97                                "Workspace Package: {}",
98                                path.as_os_str().to_str().unwrap()
99                            )),
100                        ))?;
101
102                let workspace = workspace.as_table().ok_or_else(|| {
103                    set_err(
104                        ErrKind::ItemInvalid(workspace.into()),
105                        Some("Tried to get package table"),
106                    )
107                })?;
108                let package = workspace
109                    .get("package")
110                    .ok_or(ErrKind::PackageNotFound.to_error(path, Some("")))?;
111
112                let package = package.as_table().ok_or_else(|| {
113                    set_err(
114                        ErrKind::ItemInvalid(package.into()),
115                        Some("Tried to get package table"),
116                    )
117                })?;
118                match package.get("version").ok_or(set_err(
119                    ErrKind::NotFound(*self),
120                    Some("Package table located."),
121                ))? {
122                    toml_edit::Item::Value(value) => Version::parse(value.as_str().unwrap())
123                        .map_err(|e| set_err(e.into(), Some("Workspace Version"))),
124                    item => Err(set_err(ErrKind::ItemInvalid(item.into()), None)),
125                }
126            }
127        };
128        let version = ret?;
129        current_span!().record("path", path.as_os_str().to_str().unwrap_or_default());
130        current_span!().record("version", &version.to_string());
131        info!("Version found");
132
133        Ok(version)
134    }
135
136    #[track_caller]
137    #[instrument(skip_all, fields(version, path))]
138    pub fn set_version<'a>(
139        &self,
140        cargo_toml: &mut CargoFile<ReadToml>,
141        version: &Version,
142    ) -> Result<(), VersionlocationError> {
143        use crate::manifest::error::VersionLocationErrorKind as ErrKind;
144        let path = cargo_toml.path().to_path_buf();
145
146        let set_err = |kind: ErrKind, context: Option<&'static str>| kind.to_error(&path, context);
147
148        let _span = current_span!();
149
150        let document = cargo_toml
151            .contents_mut()
152            .expect("Can't call this function without the document read.");
153
154        trace!("have document");
155
156        let ret = match self {
157            VersionLocation::Package => {
158                let package = document
159                    .get_mut("package")
160                    .ok_or(set_err(ErrKind::PackageNotFound, None))?;
161                let pack_kind = ItemType::from(&*package);
162
163                let package_table = package.as_table_mut().ok_or_else(|| {
164                    set_err(
165                        ErrKind::ItemInvalid(pack_kind),
166                        Some("Tried to get package table"),
167                    )
168                })?;
169
170                match package_table.get_mut("version").ok_or(set_err(
171                    ErrKind::NotFound(*self),
172                    Some("Package table located."),
173                ))? {
174                    toml_edit::Item::Value(value) => Ok(*value = version.to_string().into()),
175                    item => Err(set_err(
176                        ErrKind::ItemInvalid(item.into()),
177                        Some("Invalid itemtype for setting package version."),
178                    )),
179                }
180            }
181            VersionLocation::WorkspacePackage => {
182                let workspace = document
183                    .get_mut("workspace")
184                    .ok_or(set_err(ErrKind::WorkspaceNotFound, None))?;
185
186                let ws_kind = ItemType::from(&*workspace);
187                let workspace = workspace.as_table_mut().ok_or_else(|| {
188                    set_err(
189                        ErrKind::ItemInvalid(ws_kind.into()),
190                        Some("Tried to get package table"),
191                    )
192                })?;
193                let package = workspace
194                    .get_mut("package")
195                    .ok_or(set_err(ErrKind::PackageNotFound, None))?;
196                let pack_kind = ItemType::from(&*package);
197
198                let package = package.as_table_mut().ok_or_else(|| {
199                    set_err(
200                        ErrKind::ItemInvalid(pack_kind.into()),
201                        Some("Tried to get package table"),
202                    )
203                })?;
204                match package.get_mut("version").ok_or(set_err(
205                    ErrKind::NotFound(*self),
206                    Some("Package table located."),
207                ))? {
208                    toml_edit::Item::Value(value) => Ok(*value = version.to_string().into()),
209                    item => Err(set_err(
210                        ErrKind::ItemInvalid(item.into()),
211                        Some("Invalid itemtype for setting workspace version."),
212                    )),
213                }
214            }
215        };
216        let _version = ret?;
217        current_span!().record("path", (&path).as_os_str().to_str().unwrap_or_default());
218        info!("Version set: {version}");
219        Ok(())
220    }
221}