mongo_embedded/
downloader.rs

1use anyhow::{anyhow, Result};
2use std::env;
3
4#[derive(Debug, Clone)]
5pub enum Os {
6    Linux,
7    MacOs,
8    Windows,
9}
10
11#[derive(Debug, Clone)]
12pub enum Arch {
13    X86_64,
14    Aarch64,
15}
16
17pub struct MongoUrl {
18    pub url: String,
19    pub filename: String,
20}
21
22pub fn get_os() -> Result<Os> {
23    match env::consts::OS {
24        "linux" => Ok(Os::Linux),
25        "macos" => Ok(Os::MacOs),
26        "windows" => Ok(Os::Windows),
27        os => Err(anyhow!("Unsupported OS: {}", os)),
28    }
29}
30
31pub fn get_arch() -> Result<Arch> {
32    match env::consts::ARCH {
33        "x86_64" => Ok(Arch::X86_64),
34        "aarch64" => Ok(Arch::Aarch64),
35        arch => Err(anyhow!("Unsupported Architecture: {}", arch)),
36    }
37}
38
39pub fn get_download_url(version: &str) -> Result<MongoUrl> {
40    let os = get_os()?;
41    let arch = get_arch()?;
42
43    let (_platform, package_format) = match (&os, &arch) {
44        (Os::Linux, Arch::X86_64) => ("linux-x86_64", "tgz"),
45        (Os::Linux, Arch::Aarch64) => ("linux-aarch64", "tgz"),
46        (Os::MacOs, Arch::X86_64) => ("macos-x86_64", "tgz"),
47        (Os::MacOs, Arch::Aarch64) => ("macos-aarch64", "tgz"),
48        (Os::Windows, Arch::X86_64) => ("windows-x86_64", "zip"),
49        _ => return Err(anyhow!("Unsupported OS/Arch combination")),
50    };
51
52    // Note: This is a simplified URL constructor. 
53    // Real MongoDB URLs are more complex and depend on specific distros for Linux.
54    // We might need to handle specific linux distros. 
55    // For now, let's try to find a generic linux binary or handle specific common ones.
56    
57    // Example: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2004-7.0.2.tgz
58    // Example: https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-7.0.2.tgz
59    // Example: https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-7.0.2.zip
60
61    // For generic linux, we often need to specify a distro.
62    // Let's assume ubuntu2204 for now for linux x64 as a safeish default or try to detect.
63    // Or use the "generic" linux legacy if available, but modern mongo usually target distros.
64    
65    let base_url = "https://fastdl.mongodb.org";
66    
67    let _filename = match os {
68        Os::Linux => format!("mongodb-linux-{}-{}.{}", "x86_64-ubuntu2204", version, package_format), // HARDCODING ubuntu2204 for x64 linux for now
69        Os::MacOs => format!("mongodb-macos-{}-{}.{}", "x86_64", version, package_format), // Need to fix arch for mac
70        Os::Windows => format!("mongodb-windows-x86_64-{}.{}", version, package_format),
71    };
72
73    // Refined logic
74    let url = match (&os, &arch) {
75        (Os::Linux, Arch::X86_64) => format!("{}/linux/mongodb-linux-x86_64-ubuntu2204-{}.tgz", base_url, version),
76        (Os::Linux, Arch::Aarch64) => format!("{}/linux/mongodb-linux-aarch64-ubuntu2204-{}.tgz", base_url, version),
77        (Os::MacOs, Arch::X86_64) => format!("{}/osx/mongodb-macos-x86_64-{}.tgz", base_url, version),
78        (Os::MacOs, Arch::Aarch64) => format!("{}/osx/mongodb-macos-aarch64-{}.tgz", base_url, version),
79        (Os::Windows, Arch::X86_64) => format!("{}/windows/mongodb-windows-x86_64-{}.zip", base_url, version),
80        _ => return Err(anyhow!("Unsupported OS/Arch combination")),
81    };
82
83    let filename = url.split('/').last().unwrap().to_string();
84
85    Ok(MongoUrl {
86        url,
87        filename,
88    })
89}
90
91
92pub struct DownloadProgress {
93    pub downloaded: u64,
94    pub total: Option<u64>,
95    pub percentage: Option<f32>,
96}
97
98pub async fn download_file(url: &str, destination: &std::path::Path) -> Result<()> {
99    download_file_with_callback(url, destination, |_| {}).await
100}
101
102pub async fn download_file_with_callback<F>(
103    url: &str,
104    destination: &std::path::Path,
105    mut callback: F,
106) -> Result<()>
107where
108    F: FnMut(DownloadProgress),
109{
110    use std::io::Write;
111    use std::fs::File;
112
113    let response = reqwest::get(url).await?;
114    let total = response.content_length();
115
116    let mut part_path = destination.to_path_buf();
117    part_path.set_extension("part");
118
119    let mut file = File::create(&part_path)?;
120    let mut downloaded: u64 = 0;
121
122    let mut stream = response;
123    while let Some(chunk) = stream.chunk().await? {
124        file.write_all(&chunk)?;
125        downloaded += chunk.len() as u64;
126
127        let percentage = total.map(|t| (downloaded as f32 / t as f32) * 100.0);
128        callback(DownloadProgress {
129            downloaded,
130            total,
131            percentage,
132        });
133    }
134
135    std::fs::rename(part_path, destination)?;
136
137    Ok(())
138}