Skip to main content

mvm_cli/
http.rs

1use std::io::Write;
2use std::path::Path;
3
4use anyhow::{Context, Result};
5
6/// Fetch a URL and return the response body as a string.
7pub 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
28/// Fetch a URL and parse the response as JSON.
29pub 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
51/// Download a URL to a file on disk. Shows no progress (use for small files).
52pub 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}