diff --git a/tools/check_crate_updates/src/main.rs b/tools/check_crate_updates/src/main.rs index fb2b7e36..98557e8d 100644 --- a/tools/check_crate_updates/src/main.rs +++ b/tools/check_crate_updates/src/main.rs @@ -1,9 +1,16 @@ +//! This tool loops over each manifest for the images and for each Bevy crate, checks the support +//! table in its readme to see if it can be updated, if so it updates the manifest. + use anyhow::anyhow; use cached::proc_macro::cached; -use std::{env, fs}; +use std::{env, fs, str::FromStr}; use table_extract::Table; use ureq::{Agent, AgentBuilder}; +use crate::manifest::Manifest; + +mod manifest; + const EXCLUDE_CRATES: &[&str] = &["bevy", "rand", "rand_chacha", "wasm-bindgen"]; fn main() -> anyhow::Result<()> { @@ -26,29 +33,23 @@ fn main() -> anyhow::Result<()> { for path in manifest_paths { let manifest_str = fs::read_to_string(&path)?; - let mut manifest = manifest_str - .parse::() + let mut manifest = Manifest::from_str(&manifest_str) .map_err(|e| anyhow!("Failed to parse manifest at {path:?}\n{e}"))?; - // skip if no version - using main branch - if !manifest["dependencies"]["bevy"] - .as_inline_table() - .unwrap() - .contains_key("version") - { - println!("Skipping {path:?}"); + let Some(bevy) = manifest.get_dependency("bevy") else { + eprintln!("Skipping {path:?}, manifest does not contain Bevy"); continue; - } + }; - let bevy_version = manifest["dependencies"]["bevy"]["version"] - .as_str() - .unwrap(); + let Some(bevy_version) = bevy.get_version() else { + // Most likely on main branch + eprintln!("Skipping {path:?}, invalid Bevy version"); + continue; + }; - let crates = manifest["dependencies"] - .as_table() - .unwrap() - .iter() - .map(|(name, _)| name) + let crates = manifest + .get_dependency_names() + .unwrap() // we know bevy exists so it can't be empty .filter(|name| !EXCLUDE_CRATES.contains(name)) .map(|name| fetch_crate(name, agent.clone())) .inspect(|res| { @@ -124,10 +125,12 @@ fn main() -> anyhow::Result<()> { } for (name, version) in newest_versions { - if let Some(table) = manifest["dependencies"][&name].as_inline_table_mut() { - table["version"] = version.into(); - } else { - manifest["dependencies"][name] = toml_edit::value(version); + if !manifest + .get_dependency_mut(&name) + .unwrap() // name is a result from dep list so it must exist + .set_version(&version) + { + eprintln!("Failed to set value of {name} tp {version}"); } } @@ -145,7 +148,7 @@ fn main() -> anyhow::Result<()> { result = true, ty = "cached::SizedCache", create = "{ cached::SizedCache::with_size(20) }", - convert = r#"{ name.to_string() }"# + convert = r#"{ name.to_owned() }"# )] fn fetch_crate(name: &str, agent: Agent) -> anyhow::Result { agent @@ -160,7 +163,7 @@ fn fetch_crate(name: &str, agent: Agent) -> anyhow::Result { result = true, ty = "cached::SizedCache", create = "{ cached::SizedCache::with_size(20) }", - convert = r#"{ c.data.name.to_string() }"# + convert = r#"{ c.data.name.clone() }"# )] fn fetch_readme(c: &CrateResponse, agent: Agent) -> anyhow::Result { let path = &c.versions[0].readme_path; // index 0 is latest diff --git a/tools/check_crate_updates/src/manifest.rs b/tools/check_crate_updates/src/manifest.rs new file mode 100644 index 00000000..df017d64 --- /dev/null +++ b/tools/check_crate_updates/src/manifest.rs @@ -0,0 +1,81 @@ +use std::{fmt::Display, str::FromStr}; + +pub struct Manifest(toml_edit::DocumentMut); + +impl Manifest { + /// Gets the dependency from the manifest if it exists + pub fn get_dependency(&self, name: &str) -> Option { + self.0 + .get("dependencies") + .and_then(|dep| dep.get(name)) + .map(|dep| Dependency(dep)) + } + + /// Gets the dependency mutably from the manifest if it exists + pub fn get_dependency_mut(&mut self, name: &str) -> Option { + self.0 + .get_mut("dependencies") + .and_then(|dep| dep.get_mut(name)) + .map(|dep| DependencyMut(dep)) + } + + /// Gets the names of all the dependencies from the manifest + pub fn get_dependency_names<'a>(&'a self) -> Option> { + self.0 + .get("dependencies") + .and_then(|item| item.as_table()) + .map(|table| table.iter().map(|(name, _)| name)) + } +} + +pub struct Dependency<'a>(&'a toml_edit::Item); +pub struct DependencyMut<'a>(&'a mut toml_edit::Item); + +impl<'a> Dependency<'a> { + /// Gets the version of the dependency by searching first + /// the value of the entry and then as a inline table + pub fn get_version(&self) -> Option<&'a str> { + if let Some(version) = self.0.get("version").and_then(|i| i.as_str()) { + Some(version) + } else if let Some(table) = self.0.as_inline_table() { + table.get("version").and_then(|i| i.as_str()) + } else { + None + } + } +} + +impl<'a> DependencyMut<'a> { + /// Sets the version of the dependency by searching first + /// the value of the entry and then as a inline table. + /// Returns whether it was successful + pub fn set_version(&mut self, version: &str) -> bool { + if let Some(value) = self.0.get_mut("version").and_then(|i| i.as_value_mut()) { + *value = version.into(); + true + } else if let Some(value) = self + .0 + .as_inline_table_mut() + .and_then(|table| table.get_mut("version")) + { + *value = version.into(); + true + } else { + false + } + } +} + +impl FromStr for Manifest { + type Err = toml_edit::TomlError; + + fn from_str(s: &str) -> Result { + s.parse::().map(|doc| Manifest(doc)) + } +} + +impl Display for Manifest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0.to_string()) + } +}