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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Expose resolution information.

use anyhow::Result as AnyResult;
use clap::{Parser, ValueEnum};
use h3o::Resolution;
use serde::Serialize;
use std::fmt;

/// Print cell statistics per resolution.
#[derive(Parser, Debug)]
pub struct Args {
    /// h3o index.
    #[arg(short, long)]
    resolution: Option<Resolution>,

    /// Output format.
    #[arg(short, long, value_enum, default_value_t = Format::Text)]
    format: Format,

    /// Prettify the output (JSON only).
    #[arg(short, long, default_value_t = false)]
    pretty: bool,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
enum Format {
    Text,
    Json,
}

/// Run the `indexDecode` command.
pub fn run(args: &Args) -> AnyResult<()> {
    if let Some(resolution) = args.resolution {
        let info = ResolutionInfo::from(resolution);
        match args.format {
            Format::Text => println!("{info}"),
            Format::Json => crate::json::print(&info, args.pretty)?,
        }
        return Ok(());
    }

    match args.format {
        Format::Text => {
            println!("╔═{c:═>10}═╦═{c:═>15}═╦═{c:═>20}═╦═{c:═>14}═╗", c = '═');
            println!(
                "║ {:<10} ║ {:<15} ║ {:<20} ║ {:<14} ║",
                "Resolution", "Cell count", "Hexagon area", "Edge length"
            );
            println!("╠═{c:═>10}═╬═{c:═>15}═╬═{c:═>20}═╬═{c:═>14}═╣", c = '═');
            for resolution in
                Resolution::range(Resolution::Zero, Resolution::Fifteen)
            {
                let info = ResolutionInfo::from(resolution);
                let (area, area_unit, length, length_unit) = if info.resolution
                    < Resolution::Eight
                {
                    (info.hexagon_area_km2, "km2", info.edge_length_km, "km")
                } else {
                    (
                        info.hexagon_area_km2 * 1e6,
                        "m2",
                        info.edge_length_km * 1e3,
                        "m",
                    )
                };
                println!(
                    "║ {:>10} ║ {:>15} ║ {:16.3} {:<3} ║ {:11.3} {:<2} ║",
                    u8::from(info.resolution),
                    info.cell_count,
                    area,
                    area_unit,
                    length,
                    length_unit
                );
            }
            println!("╚═{c:═>10}═╩═{c:═>15}═╩═{c:═>20}═╩═{c:═>14}═╝", c = '═');
        }
        Format::Json => {
            let infos =
                Resolution::range(Resolution::Zero, Resolution::Fifteen)
                    .map(Into::into)
                    .collect::<Vec<ResolutionInfo>>();
            crate::json::print(&infos, args.pretty)?;
        }
    }

    Ok(())
}

// -----------------------------------------------------------------------------

#[derive(Serialize)]
struct ResolutionInfo {
    resolution: Resolution,
    cell_count: u64,
    hexagon_area_km2: f64,
    edge_length_km: f64,
}

impl From<Resolution> for ResolutionInfo {
    fn from(value: Resolution) -> Self {
        Self {
            resolution: value,
            cell_count: value.cell_count(),
            hexagon_area_km2: value.area_km2(),
            edge_length_km: value.edge_length_km(),
        }
    }
}

impl fmt::Display for ResolutionInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "resolution:   {}", self.resolution)?;
        writeln!(f, "cell count:   {}", self.cell_count)?;
        if self.resolution < Resolution::Eight {
            writeln!(f, "hexagon area: {} km2", self.hexagon_area_km2)?;
            write!(f, "edge length:  {} km", self.edge_length_km)
        } else {
            writeln!(f, "hexagon area: {} m2", self.hexagon_area_km2 * 1e6)?;
            write!(f, "edge length:  {} m", self.edge_length_km * 1e3)
        }
    }
}