use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct Fixture {
pub name: &'static str,
pub description: &'static str,
pub url: &'static str,
pub commit: &'static str,
pub path: &'static str,
}
impl Fixture {
pub fn raw_url(&self) -> String {
format!(
"https://raw.githubusercontent.com/{}/{}/{}",
self.url, self.commit, self.path
)
}
pub fn cache_path(&self) -> PathBuf {
PathBuf::from(env!("CARGO_TARGET_TMPDIR"))
.join("perf-fixtures")
.join(format!("{}-{}.md", self.name, &self.commit[..8]))
}
pub fn download(&self) -> Result<String, Box<dyn std::error::Error>> {
let cache_path = self.cache_path();
if cache_path.exists() {
return Ok(fs::read_to_string(cache_path)?);
}
if let Some(parent) = cache_path.parent() {
fs::create_dir_all(parent)?;
}
let content = download_with_retry(&self.raw_url(), 3)?;
fs::write(&cache_path, &content)?;
Ok(content)
}
}
fn download_with_retry(url: &str, max_retries: usize) -> Result<String, Box<dyn std::error::Error>> {
let agent = ureq::Agent::new_with_config(
ureq::config::Config::builder()
.timeout_global(Some(std::time::Duration::from_secs(30)))
.build(),
);
let mut last_error = None;
for attempt in 0..max_retries {
match agent.get(url).call() {
Ok(response) => {
let body = response.into_body().read_to_string()?;
return Ok(body);
}
Err(e) => {
eprintln!("Download attempt {} failed: {}", attempt + 1, e);
last_error = Some(e);
if attempt < max_retries - 1 {
std::thread::sleep(std::time::Duration::from_secs(1 << attempt));
}
}
}
}
Err(Box::new(last_error.unwrap()))
}
pub const FIXTURES: &[Fixture] = &[
Fixture {
name: "rust-book",
description: "The Rust Programming Language book - chapter on ownership",
url: "rust-lang/book",
commit: "c06006157b14b3d47882530fcb94e0b3c304f07d",
path: "src/ch04-01-what-is-ownership.md",
},
Fixture {
name: "awesome-rust",
description: "Awesome Rust - massive README with many links and lists",
url: "rust-unofficial/awesome-rust",
commit: "main",
path: "README.md",
},
Fixture {
name: "rust-rfc",
description: "Rust RFC - technical specification document",
url: "rust-lang/rfcs",
commit: "master",
path: "text/0002-rfc-process.md",
},
Fixture {
name: "mdbook-guide",
description: "mdBook user guide - docs with many code examples",
url: "rust-lang/mdBook",
commit: "master",
path: "guide/src/README.md",
},
Fixture {
name: "blog-post",
description: "Rust blog post - narrative with code examples",
url: "rust-lang/blog.rust-lang.org",
commit: "master",
path: "posts/2024-01-09-Rust-1.75.0.md",
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_download_fixtures() {
for fixture in FIXTURES {
println!("Downloading: {} - {}", fixture.name, fixture.description);
match fixture.download() {
Ok(content) => {
println!(" Downloaded {} bytes", content.len());
assert!(!content.is_empty(), "Content should not be empty");
}
Err(e) => {
eprintln!(" Failed to download: {e}");
}
}
}
}
#[test]
fn test_cache_path_generation() {
let fixture = &FIXTURES[0];
let path = fixture.cache_path();
assert!(path.to_string_lossy().contains("rust-book"));
assert!(path.to_string_lossy().contains(&fixture.commit[..8]));
}
#[test]
fn test_raw_url_generation() {
let fixture = &FIXTURES[0];
let url = fixture.raw_url();
assert!(url.starts_with("https://raw.githubusercontent.com/"));
assert!(url.contains(fixture.commit));
assert!(url.contains(fixture.path));
}
}