1use std::io::Write;
2use std::path::Path;
3
4use anyhow::{Context, Result};
5
6pub fn fetch_text(url: &str) -> Result<String> {
8 let client = reqwest::blocking::Client::builder()
9 .user_agent(concat!("mvm/", env!("CARGO_PKG_VERSION")))
10 .timeout(std::time::Duration::from_secs(30))
11 .build()
12 .context("Failed to build HTTP client")?;
13
14 let resp = client
15 .get(url)
16 .send()
17 .with_context(|| format!("HTTP request failed: {}", url))?;
18
19 let status = resp.status();
20 if !status.is_success() {
21 anyhow::bail!("HTTP {} for {}", status, url);
22 }
23
24 resp.text()
25 .with_context(|| format!("Failed to read response body from {}", url))
26}
27
28pub fn fetch_json(url: &str) -> Result<serde_json::Value> {
30 let client = reqwest::blocking::Client::builder()
31 .user_agent(concat!("mvm/", env!("CARGO_PKG_VERSION")))
32 .timeout(std::time::Duration::from_secs(30))
33 .build()
34 .context("Failed to build HTTP client")?;
35
36 let resp = client
37 .get(url)
38 .header("Accept", "application/json")
39 .send()
40 .with_context(|| format!("HTTP request failed: {}", url))?;
41
42 let status = resp.status();
43 if !status.is_success() {
44 anyhow::bail!("HTTP {} for {}", status, url);
45 }
46
47 resp.json::<serde_json::Value>()
48 .with_context(|| format!("Failed to parse JSON from {}", url))
49}
50
51pub fn download_file(url: &str, dest: &Path) -> Result<()> {
53 let client = reqwest::blocking::Client::builder()
54 .user_agent(concat!("mvm/", env!("CARGO_PKG_VERSION")))
55 .timeout(std::time::Duration::from_secs(600))
56 .build()
57 .context("Failed to build HTTP client")?;
58
59 let resp = client
60 .get(url)
61 .send()
62 .with_context(|| format!("Download failed: {}", url))?;
63
64 let status = resp.status();
65 if !status.is_success() {
66 anyhow::bail!("HTTP {} downloading {}", status, url);
67 }
68
69 let bytes = resp
70 .bytes()
71 .with_context(|| format!("Failed to read download body: {}", url))?;
72
73 let mut file = std::fs::File::create(dest)
74 .with_context(|| format!("Failed to create file: {}", dest.display()))?;
75
76 file.write_all(&bytes)
77 .with_context(|| format!("Failed to write to: {}", dest.display()))?;
78
79 Ok(())
80}