h3o_cli/commands/
grid_disk.rs

1//! Expose [`CellIndex::grid_disk`].
2
3use anyhow::{Context, Result as AnyResult};
4use clap::{Parser, ValueEnum};
5use h3o::CellIndex;
6use serde::Serialize;
7
8/// Print cell indexes `radius` distance away from the origin.
9///
10/// The command reads cell indexes from stdin until EOF and outputs
11/// the cell indexes within k-ring `radius` to stdout.
12#[derive(Parser, Debug)]
13pub struct Args {
14    /// Cell index.
15    #[arg(short, long)]
16    origin: Option<CellIndex>,
17
18    /// Radius (in hexagons).
19    #[arg(short, long)]
20    radius: u32,
21
22    /// Also return the distance from the origin.
23    #[arg(short, long, default_value_t = false)]
24    distance: bool,
25
26    /// Output format.
27    #[arg(short, long, value_enum, default_value_t = Format::Text)]
28    format: Format,
29
30    /// Prettify the output (JSON only).
31    #[arg(short, long, default_value_t = false)]
32    pretty: bool,
33}
34
35#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
36enum Format {
37    Text,
38    Json,
39}
40
41/// Run the `gridDisk` command.
42pub fn run(args: &Args) -> AnyResult<()> {
43    let indexes = crate::utils::get_cell_indexes(args.origin);
44
45    match args.format {
46        Format::Text => disks_to_text(indexes, args.radius, args.distance),
47        Format::Json => {
48            disks_to_json(indexes, args.radius, args.distance, args.pretty)
49        }
50    }
51    .context("gridDisk")?;
52
53    Ok(())
54}
55
56/// Print disks as plain text.
57fn disks_to_text(
58    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
59    radius: u32,
60    with_distance: bool,
61) -> AnyResult<()> {
62    let disks = indexes
63        .into_iter()
64        .map(|input| input.map(|index| index.grid_disk_distances_safe(radius)));
65
66    if with_distance {
67        for disk in disks {
68            let disk = disk?;
69            for (index, distance) in disk {
70                println!("{index} {distance}");
71            }
72        }
73    } else {
74        for disk in disks {
75            let disk = disk?;
76            for (index, _) in disk {
77                println!("{index}");
78            }
79        }
80    }
81
82    Ok(())
83}
84
85/// Print disks as JSON.
86fn disks_to_json(
87    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
88    radius: u32,
89    with_distance: bool,
90    pretty: bool,
91) -> AnyResult<()> {
92    let disks = indexes.into_iter().map(|input| {
93        input.map(|origin| {
94            origin
95                .grid_disk_distances_safe(radius)
96                .map(|(index, distance)| {
97                    (crate::json::CellIndex::from(index), distance)
98                })
99        })
100    });
101
102    if with_distance {
103        #[derive(Serialize)]
104        struct Neighbor {
105            index: crate::json::CellIndex,
106            distance: u32,
107        }
108        let disks = disks
109            .map(|result| {
110                result.map(|disk| {
111                    disk.map(|(index, distance)| Neighbor { index, distance })
112                        .collect::<Vec<_>>()
113                })
114            })
115            .collect::<AnyResult<Vec<_>>>()?;
116        crate::json::print(&disks, pretty)
117    } else {
118        let disks = disks
119            .map(|result| {
120                result.map(|disk| {
121                    disk.map(|(index, _)| index).collect::<Vec<_>>()
122                })
123            })
124            .collect::<AnyResult<Vec<_>>>()?;
125        crate::json::print(&disks, pretty)
126    }
127}