tuwunel_admin/query/raw/
mod.rs1mod clear;
2mod compact;
3mod count;
4mod del;
5mod get;
6mod iter;
7mod keys;
8mod keys_sizes;
9mod keys_total;
10mod maps;
11mod sequence;
12mod vals_sizes;
13mod vals_total;
14
15use std::{fmt::Write, sync::Arc};
16
17use clap::Subcommand;
18use tuwunel_core::{Result, err, itertools::Itertools, utils::math::Expected};
19use tuwunel_database::Map;
20use tuwunel_service::Services;
21
22use crate::admin_command_dispatch;
23
24#[admin_command_dispatch(handler_prefix = "raw")]
25#[derive(Debug, Subcommand)]
26pub(crate) enum RawCommand {
28 Maps,
30
31 Sequence,
33
34 Get {
36 map: String,
38
39 key: String,
41
42 #[arg(long, short)]
44 base64: bool,
45 },
46
47 Keys {
49 map: String,
51
52 prefix: Option<String>,
54
55 #[arg(short, long)]
57 limit: Option<usize>,
58
59 #[arg(short, long)]
61 from: Option<String>,
62
63 #[arg(short, long, default_value("false"))]
65 backwards: bool,
66 },
67
68 Iter {
70 map: String,
72
73 prefix: Option<String>,
75
76 #[arg(short, long)]
78 limit: Option<usize>,
79
80 #[arg(short, long)]
82 from: Option<String>,
83
84 #[arg(short, long, default_value("false"))]
86 backwards: bool,
87 },
88
89 KeysSizes {
91 map: Option<String>,
93
94 prefix: Option<String>,
96 },
97
98 KeysTotal {
100 map: Option<String>,
102
103 prefix: Option<String>,
105 },
106
107 ValsSizes {
109 map: Option<String>,
111
112 prefix: Option<String>,
114 },
115
116 ValsTotal {
118 map: Option<String>,
120
121 prefix: Option<String>,
123 },
124
125 Count {
127 map: Option<String>,
129
130 prefix: Option<String>,
132 },
133
134 Del {
136 map: String,
138
139 key: String,
141 },
142
143 Clear {
145 map: String,
147
148 #[arg(long)]
150 confirm: bool,
151 },
152
153 Compact {
155 #[arg(short, long, alias("column"))]
156 maps: Option<Vec<String>>,
157
158 #[arg(long)]
159 start: Option<String>,
160
161 #[arg(long)]
162 stop: Option<String>,
163
164 #[arg(long)]
165 from: Option<usize>,
166
167 #[arg(long)]
168 into: Option<usize>,
169
170 #[arg(long)]
175 parallelism: Option<usize>,
176
177 #[arg(long, default_value("false"))]
178 exhaustive: bool,
179 },
180}
181
182fn with_map_or(map: Option<&str>, services: &Services) -> Result<Vec<Arc<Map>>> {
183 with_maps_or(
184 map.map(|map| [map])
185 .as_ref()
186 .map(<[&str; 1]>::as_slice),
187 services,
188 )
189}
190
191fn with_maps_or<S: AsRef<str>>(maps: Option<&[S]>, services: &Services) -> Result<Vec<Arc<Map>>> {
192 Ok(if let Some(maps) = maps {
193 maps.iter()
194 .map(|map| {
195 let map = map.as_ref();
196 services
197 .db
198 .get(map)
199 .cloned()
200 .map_err(|_| err!("map {map} not found"))
201 })
202 .try_collect()?
203 } else {
204 services.db.iter().map(|x| x.1.clone()).collect()
205 })
206}
207
208#[expect(clippy::as_conversions)]
209fn encode(data: &[u8]) -> String {
210 let mut res = String::with_capacity(data.len().expected_mul(4));
211
212 for byte in data {
213 if *byte < 0x20 || *byte > 0x7E {
214 _ = write!(res, "\\x{byte:02x}");
215 } else {
216 res.push(*byte as char);
217 }
218 }
219
220 res
221}