1use std::{
2 str::FromStr,
3 path::{ Path, PathBuf },
4 time::Duration,
5};
6use clap::{App, SubCommand, Arg};
7use cyfs_base::*;
8use crate::stack::Stack;
9
10pub fn debug_command_line() -> clap::App<'static, 'static> {
11 App::new("bdt-debug")
12 .about("bdt stack debug")
13 .arg(Arg::with_name("host")
14 .short("h")
15 .long("host")
16 .value_name("host")
17 .help("connect remote host")
18 .default_value("127.0.0.1"))
19 .arg(Arg::with_name("port")
20 .short("p")
21 .long("port")
22 .value_name("port")
23 .help("local server port")
24 .default_value("12345"))
25 .subcommand(SubCommand::with_name("test"))
26 .subcommand(SubCommand::with_name("ping")
27 .arg(Arg::with_name("remote").required(true))
28 .arg(Arg::with_name("count").required(true))
29 .arg(Arg::with_name("timeout").required(true))
30 )
31 .subcommand(SubCommand::with_name("nc")
32 .arg(Arg::with_name("remote").required(true))
33 .arg(Arg::with_name("port").required(true))
34 .arg(Arg::with_name("bench").required(false))
35 .arg(Arg::with_name("task").required(false))
36 )
37 .subcommand(SubCommand::with_name("get_chunk")
38 .arg(Arg::with_name("remotes").required(true))
39 .arg(Arg::with_name("timeout").required(true))
40 .arg(Arg::with_name("chunk_id").required(true))
41 .arg(Arg::with_name("local_path").required(false))
42 )
43 .subcommand(SubCommand::with_name("get_file")
44 .arg(Arg::with_name("remotes").required(true))
45 .arg(Arg::with_name("timeout").required(true))
46 .arg(Arg::with_name("file_id").required(true))
47 .arg(Arg::with_name("local_path").required(true))
48 )
49 .subcommand(SubCommand::with_name("put_chunk")
50 .arg(Arg::with_name("local_path").required(true))
51 )
52 .subcommand(SubCommand::with_name("put_file")
53 .arg(Arg::with_name("local_path").required(true))
54 )
55 .subcommand(SubCommand::with_name("sn_conn_status")
56 .arg(Arg::with_name("timeout").required(true))
57 )
58 .subcommand(SubCommand::with_name("bench_datagram")
59 .arg(Arg::with_name("remote").required(true))
60 .arg(Arg::with_name("plaintext").required(true))
61 .arg(Arg::with_name("timeout").required(true))
62 )
63}
64
65pub enum DebugCommand {
66 Test(DebugCommandTest),
67 Ping(DebugCommandPing),
68 Nc(DebugCommandNc),
69 GetChunk(DebugCommandGetChunk),
70 GetFile(DebugCommandGetFile),
71 PutChunk(DebugCommandPutChunk),
72 PutFile(DebugCommandPutFile),
73 SnConnStatus(DebugCommandSnConnStatus),
74 BenchDatagram(DebugCommandBenchDatagram),
75}
76
77impl DebugCommand {
78 pub async fn from_str(stack: &Stack, command: &str) -> Result<Self, String> {
79 let params = debug_command_line().get_matches_from_safe(command.split(" "))
80 .map_err(|err| err.message)?;
81 let subcommand = params.subcommand_name().ok_or_else(|| "no subcommand\r\n".to_string())?;
82 match subcommand {
83 "test" => {
84 let _ = params.subcommand_matches("test").unwrap();
85 Ok(Self::Test(DebugCommandTest{}))
86 },
87 "ping" => {
88 let subcommand = params.subcommand_matches("ping").unwrap();
89 let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
90 .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
91 let count = u32::from_str(subcommand.value_of("count").unwrap()).unwrap();
92 let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
93
94 Ok(Self::Ping(DebugCommandPing {
95 remote,
96 count,
97 timeout: Duration::from_secs(timeout)
98 }))
99 },
100 "nc" => {
101 let subcommand = params.subcommand_matches("nc").unwrap();
102 let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
103 .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
104 let port = u16::from_str(subcommand.value_of("port").unwrap()).unwrap();
105 let bench = u32::from_str(subcommand.value_of("bench").unwrap_or("0")).unwrap();
106 let task_num = u32::from_str(subcommand.value_of("task").unwrap_or("1")).unwrap();
107 Ok(Self::Nc(DebugCommandNc {
108 remote,
109 port,
110 timeout: Duration::from_secs(8),
111 bench,
112 task_num,
113 }))
114 },
115 "get_chunk" => {
116 let subcommand = params.subcommand_matches("get_chunk").unwrap();
117 let reomotes_str = String::from_str(subcommand.value_of("remotes").unwrap()).unwrap();
118 let remotes_split = reomotes_str.split(",");
119 let mut remotes = Vec::new();
120 for remote_file in remotes_split {
121 let remote = remote_device(stack, remote_file).await
122 .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
123 remotes.push(remote.desc().clone());
124 }
125 let chunk_id_str = String::from_str(subcommand.value_of("chunk_id").unwrap()).unwrap();
126 let chunk_id = ChunkId::from_str(&chunk_id_str.as_str()).map_err(|err| format!("load chunk_id {} fail: {}\r\n",
127 chunk_id_str, err))?;
128 let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap_or("default")).unwrap();
129 let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
130 let timeout = u32::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
131
132 Ok(Self::GetChunk(DebugCommandGetChunk {
133 remotes,
134 timeout,
135 chunk_id,
136 local_path
137 }))
138 },
139 "get_file" => {
140 let subcommand = params.subcommand_matches("get_file").unwrap();
141 let reomotes_str = String::from_str(subcommand.value_of("remotes").unwrap()).unwrap();
142 let remotes_split = reomotes_str.split(",");
143 let mut remotes = Vec::new();
144 for remote_file in remotes_split {
145 let remote = remote_device(stack, remote_file).await
146 .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
147 remotes.push(remote.desc().device_id());
148 }
149 let file_id_str = String::from_str(subcommand.value_of("file_id").unwrap()).unwrap();
150 let hex_data = hex::decode(file_id_str.as_bytes()).unwrap();
151 let (file_id, _) = File::raw_decode(hex_data.as_slice()).map_err(|err| format!("load file_id {} fail: {}",
152 file_id_str, err))?;
153 let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
154 let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
155 let timeout = u32::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
156
157 Ok(Self::GetFile(DebugCommandGetFile {
158 remotes,
159 timeout,
160 file_id,
161 local_path
162 }))
163 },
164 "put_chunk" => {
165 let subcommand = params.subcommand_matches("put_chunk").unwrap();
166 let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
167 let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
168
169 Ok(Self::PutChunk(DebugCommandPutChunk {
170 local_path
171 }))
172 },
173 "put_file" => {
174 let subcommand = params.subcommand_matches("put_file").unwrap();
175 let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
176 let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
177
178 Ok(Self::PutFile(DebugCommandPutFile {
179 local_path,
180 }))
181 },
182 "sn_conn_status" => {
183 let subcommand = params.subcommand_matches("sn_conn_status").unwrap();
184 let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
185 Ok(Self::SnConnStatus(DebugCommandSnConnStatus {
186 timeout_sec: timeout,
187 }))
188 },
189 "bench_datagram" => {
190 let subcommand = params.subcommand_matches("bench_datagram").unwrap();
191 let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
192 .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
193 let plaintext = u32::from_str(subcommand.value_of("plaintext").unwrap()).unwrap();
194 let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
195
196 Ok(Self::BenchDatagram(DebugCommandBenchDatagram {
197 remote,
198 timeout: Duration::from_secs(timeout),
199 plaintext: plaintext != 0
200 }))
201 }
202 _ => {
203 Err(format!("invalid subcommand {}\r\n", subcommand))
204 }
205 }
206 }
207}
208
209pub struct DebugCommandTest {}
210
211pub struct DebugCommandBenchDatagram {
212 pub remote: Device,
213 pub timeout: Duration,
214 pub plaintext: bool,
215}
216
217pub struct DebugCommandPing {
218 pub remote: Device,
219 pub count: u32,
220 pub timeout: Duration
221}
222
223#[derive(Clone, Debug)]
224pub struct DebugCommandNc {
225 pub remote: Device,
226 pub port: u16,
227 pub timeout: Duration,
228 pub bench: u32,
229 pub task_num: u32,
230}
231
232pub struct DebugCommandGetChunk {
233 pub remotes: Vec<DeviceDesc>,
234 pub timeout: u32,
235 pub chunk_id: ChunkId,
236 pub local_path: PathBuf,
237}
238
239pub struct DebugCommandGetFile {
240 pub remotes: Vec<DeviceId>,
241 pub timeout: u32,
242 pub file_id: File,
243 pub local_path: PathBuf,
244}
245
246pub struct DebugCommandPutChunk {
247 pub local_path: PathBuf,
248}
249
250pub struct DebugCommandPutFile {
251 pub local_path: PathBuf,
252}
253
254pub struct DebugCommandSnConnStatus {
255 pub timeout_sec: u64,
256}
257
258async fn remote_device(
259 stack: &Stack,
260 str: &str) -> BuckyResult<Device> {
261 if let Ok(device_id) = DeviceId::from_str(str) {
262 if let Some(device) = stack.device_cache().get(&device_id).await {
263 Ok(device)
264 } else {
265 Err(BuckyError::new(BuckyErrorCode::NotFound, "not found"))
266 }
267 } else {
268 let path = Path::new(str);
269 if !path.exists() {
270 Err(BuckyError::new(BuckyErrorCode::NotFound, "not found"))
271 } else {
272 let mut buf = vec![];
273 let (device, _) = Device::decode_from_file(&path, &mut buf)?;
274 let device_id = device.desc().device_id();
275 if stack.device_cache().get(&device_id).await.is_none() {
276 stack.device_cache().add(&device_id, &device);
277 }
278 Ok(device)
279 }
280 }
281}
282
283
284
285
286
287
288
289
290
291
292
293