embeddenator_workspace/
cargo.rs1use anyhow::{Context, Result};
4use semver::Version;
5use std::path::{Path, PathBuf};
6use toml_edit::{value, DocumentMut, Item};
7
8#[derive(Debug, Clone)]
10pub struct CargoManifest {
11 pub path: PathBuf,
12 pub package_name: String,
13 pub version: Version,
14 pub dependencies: Vec<Dependency>,
15 document: DocumentMut,
16}
17
18#[derive(Debug, Clone)]
20pub struct Dependency {
21 pub name: String,
22 pub version: Option<Version>,
23 pub dep_type: DependencyType,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum DependencyType {
28 Normal,
29 Dev,
30 Build,
31}
32
33impl CargoManifest {
34 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
36 let path = path.as_ref();
37 let content = std::fs::read_to_string(path)
38 .with_context(|| format!("Failed to read {}", path.display()))?;
39 let document: DocumentMut = content
40 .parse()
41 .with_context(|| format!("Failed to parse {}", path.display()))?;
42
43 let package_name = document["package"]["name"]
44 .as_str()
45 .ok_or_else(|| anyhow::anyhow!("Missing package.name in {}", path.display()))?
46 .to_string();
47
48 let version_str = document["package"]["version"]
49 .as_str()
50 .ok_or_else(|| anyhow::anyhow!("Missing package.version in {}", path.display()))?;
51
52 let version = Version::parse(version_str)
53 .with_context(|| format!("Invalid version '{}' in {}", version_str, path.display()))?;
54
55 let mut dependencies = Vec::new();
56
57 if let Some(Item::Table(deps)) = document.get("dependencies") {
59 for (name, item) in deps.iter() {
60 if let Some(dep) = Self::parse_dependency(name, item, DependencyType::Normal) {
61 dependencies.push(dep);
62 }
63 }
64 }
65
66 if let Some(Item::Table(deps)) = document.get("dev-dependencies") {
68 for (name, item) in deps.iter() {
69 if let Some(dep) = Self::parse_dependency(name, item, DependencyType::Dev) {
70 dependencies.push(dep);
71 }
72 }
73 }
74
75 if let Some(Item::Table(deps)) = document.get("build-dependencies") {
77 for (name, item) in deps.iter() {
78 if let Some(dep) = Self::parse_dependency(name, item, DependencyType::Build) {
79 dependencies.push(dep);
80 }
81 }
82 }
83
84 Ok(Self {
85 path: path.to_path_buf(),
86 package_name,
87 version,
88 dependencies,
89 document,
90 })
91 }
92
93 fn parse_dependency(name: &str, item: &Item, dep_type: DependencyType) -> Option<Dependency> {
94 let version = match item {
95 Item::Value(val) if val.is_str() => {
96 val.as_str().and_then(|s| Version::parse(s).ok())
98 }
99 Item::Table(_) => {
100 item.get("version")
102 .and_then(|v| v.as_str())
103 .and_then(|s| Version::parse(s).ok())
104 }
105 _ => None,
106 };
107
108 Some(Dependency {
109 name: name.to_string(),
110 version,
111 dep_type,
112 })
113 }
114
115 pub fn set_version(&mut self, new_version: &Version) -> Result<()> {
117 self.version = new_version.clone();
118
119 if let Some(package) = self.document.get_mut("package") {
120 if let Some(pkg_table) = package.as_table_mut() {
121 pkg_table["version"] = value(new_version.to_string());
122 }
123 }
124
125 Ok(())
126 }
127
128 pub fn update_dependency(&mut self, dep_name: &str, new_version: &Version) -> Result<()> {
130 let sections = [
131 ("dependencies", DependencyType::Normal),
132 ("dev-dependencies", DependencyType::Dev),
133 ("build-dependencies", DependencyType::Build),
134 ];
135
136 for (section, dep_type) in §ions {
137 if let Some(deps) = self.document.get_mut(section) {
138 if let Some(deps_table) = deps.as_table_mut() {
139 if let Some(dep_item) = deps_table.get_mut(dep_name) {
140 Self::update_dep_item_static(dep_item, new_version)?;
141
142 if let Some(dep) = self
144 .dependencies
145 .iter_mut()
146 .find(|d| d.name == dep_name && &d.dep_type == dep_type)
147 {
148 dep.version = Some(new_version.clone());
149 }
150 }
151 }
152 }
153 }
154
155 Ok(())
156 }
157
158 fn update_dep_item_static(item: &mut Item, new_version: &Version) -> Result<()> {
159 match item {
160 Item::Value(val) if val.is_str() => {
161 *item = value(new_version.to_string());
163 }
164 Item::Table(_) => {
165 if let Some(version_item) = item.get_mut("version") {
167 *version_item = value(new_version.to_string());
168 }
169 }
170 _ => {}
171 }
172 Ok(())
173 }
174
175 pub fn save(&self) -> Result<()> {
177 std::fs::write(&self.path, self.document.to_string())
178 .with_context(|| format!("Failed to write {}", self.path.display()))?;
179 Ok(())
180 }
181
182 pub fn embeddenator_dependencies(&self) -> Vec<&Dependency> {
184 self.dependencies
185 .iter()
186 .filter(|d| d.name.starts_with("embeddenator-"))
187 .collect()
188 }
189}
190
191#[cfg(test)]
192#[path = "cargo_tests.rs"]
193mod tests;