1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::path::PathBuf;

use crate::shim::sector::SectorSize;
use crate::utils::proofs_api::{get_params_default, SectorSizeOpt};

use crate::cli::subcommands::cli_error_and_die;
use crate::cli_shared::read_config;

#[allow(missing_docs)]
#[derive(Debug, clap::Args)]
pub struct FetchCommands {
    /// Download all proof parameters
    #[arg(short, long)]
    all: bool,
    /// Download only verification keys
    #[arg(short, long)]
    keys: bool,
    /// Print out download location instead of downloading files
    #[arg(short, long)]
    dry_run: bool,
    /// Size in bytes
    params_size: Option<String>,
    /// Optional TOML file containing forest daemon configuration
    #[arg(short, long)]
    pub config: Option<PathBuf>,
}

impl FetchCommands {
    pub async fn run(self) -> anyhow::Result<()> {
        let (_, config) = read_config(self.config.as_ref(), None)?;

        let sizes = if self.all {
            SectorSizeOpt::All
        } else if let Some(size) = &self.params_size {
            let sector_size = ram_to_int(size)?;
            SectorSizeOpt::Size(sector_size)
        } else if self.keys {
            SectorSizeOpt::Keys
        } else {
            cli_error_and_die(
                "Sector size option must be chosen. Choose between --all, --keys, or <size>",
                1,
            );
        };

        get_params_default(&config.client.data_dir, sizes, self.dry_run).await
    }
}

/// Converts a human readable string to a `u64` size.
fn ram_to_int(size: &str) -> anyhow::Result<SectorSize> {
    // * there is no library to do this, but if other sector sizes are supported in
    //   future
    // this should probably be changed to parse from string to `SectorSize`
    let size = size.to_lowercase();
    let trimmed = size.trim_end_matches('b');

    type SectorSize = crate::shim::sector::SectorSize;

    match trimmed {
        "2048" | "2ki" => Ok(SectorSize::_2KiB),
        "8388608" | "8mi" => Ok(SectorSize::_8MiB),
        "536870912" | "512mi" => Ok(SectorSize::_512MiB),
        "34359738368" | "32gi" => Ok(SectorSize::_32GiB),
        "68719476736" | "64gi" => Ok(SectorSize::_64GiB),
        _ => Err(anyhow::Error::msg(format!(
            "Failed to parse: {size}. Must be a valid sector size"
        ))),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    type SectorSize = crate::shim::sector::SectorSize;

    #[test]
    fn ram_str_conversions() {
        assert_eq!(ram_to_int("2048").unwrap(), SectorSize::_2KiB);
        assert_eq!(ram_to_int("2048B").unwrap(), SectorSize::_2KiB);
        assert_eq!(ram_to_int("2kib").unwrap(), SectorSize::_2KiB);
        assert_eq!(ram_to_int("8Mib").unwrap(), SectorSize::_8MiB);
        assert_eq!(ram_to_int("512MiB").unwrap(), SectorSize::_512MiB);
        assert_eq!(ram_to_int("32Gi").unwrap(), SectorSize::_32GiB);
        assert_eq!(ram_to_int("32GiB").unwrap(), SectorSize::_32GiB);
        assert_eq!(ram_to_int("64Gib").unwrap(), SectorSize::_64GiB);
    }
}