Skip to content

Commit 70d11e6

Browse files
committed
Adopt new method-resolver API from libshvclient
Switch request dispatching from the old string-based method matching to the new strong typed API. This adoption strengthens type-checking, eliminates the need to manually return method names, and removes redundant Ok(...) wrapping.
1 parent fbfcc85 commit 70d11e6

3 files changed

Lines changed: 98 additions & 92 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ name = "hp"
1010
tokio = { version = "1.48.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] }
1111
shvproto = { git = "https://github.com/silicon-heaven/libshvproto-rs", tag = "3.6.22" }
1212
shvrpc = { git = "https://github.com/silicon-heaven/libshvrpc-rs", tag = "5.0.0" }
13-
shvclient = { git = "https://github.com/silicon-heaven/libshvclient-rs", tag = "0.20.0", features = ["tokio"] }
13+
shvclient = { git = "https://github.com/silicon-heaven/libshvclient-rs", tag = "0.21.0", features = ["tokio"] }
1414
futures = "0.3.31"
1515
log = "0.4.28"
1616
clap = { version = "4.5.53", features = ["derive"] }
@@ -42,5 +42,5 @@ humantime = "2.3.0"
4242
[dev-dependencies]
4343
async-broadcast = "0.7.2"
4444
async-trait = "0.1.80"
45-
shvclient = { git = "https://github.com/silicon-heaven/libshvclient-rs", tag = "0.20.0", features = ["mocking"] }
45+
shvclient = { git = "https://github.com/silicon-heaven/libshvclient-rs", tag = "0.21.0", features = ["mocking"] }
4646
tempfile = "3.23.0"

src/tree.rs

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use std::sync::Arc;
44

55
use async_compression::tokio::write::GzipEncoder;
66
use futures::{StreamExt, TryStreamExt};
7+
use log::error;
78
use sha1::Digest;
89
use shvclient::appnodes::DOT_APP_METHODS;
9-
use shvclient::clientnode::{rpc_error_unknown_method_on_path, LsHandlerResult};
10-
use shvclient::clientnode::{ResolvedRequest, StaticNode, METH_DIR, METH_LS};
10+
use shvclient::clientnode::{err_unresolved_request, LsHandlerResult, Method, RequestHandlerResult, UnresolvedRequest};
11+
use shvclient::clientnode::StaticNode;
1112
use shvclient::shvproto::RpcValue;
1213
use shvclient::ClientCommandSender;
1314
use shvrpc::metamethod::{AccessLevel, MetaMethod};
@@ -105,10 +106,10 @@ fn path_contains_parent_dir_references(path: impl AsRef<str>) -> bool {
105106

106107
async fn shvjournal_request_handler(
107108
path: String,
108-
method: String,
109+
method: Method,
109110
param: RpcValue,
110111
app_state: Arc<State>,
111-
) -> Result<ResolvedRequest, RpcError>
112+
) -> RequestHandlerResult
112113
{
113114
async fn get_journaldir_entries(path: impl AsRef<Path>) -> Result<ReadDirStream, RpcError> {
114115
Ok(ReadDirStream::new(
@@ -210,7 +211,7 @@ async fn shvjournal_request_handler(
210211

211212
if path_contains_parent_dir_references(&path) {
212213
// Reject parent dir references
213-
return Err(rpc_error_unknown_method_on_path(path, method));
214+
return err_unresolved_request()
214215
}
215216
let is_shvjournal_root = &path == "_shvjournal";
216217
let path = path.replacen("_shvjournal", &app_state.config.journal_dir, 1);
@@ -225,53 +226,56 @@ async fn shvjournal_request_handler(
225226
MetaMethod::new_static(METH_SYNC_INFO, 0, AccessLevel::Read, "String", "Map", &[], "syncInfo - returns info about sites' sync status\nOptionally takes a string that filters the sites by prefix.\n\nReturns: a map where they is the path of the site and the value is a map with a status string and a last updated timestamp.\n"),
226227
MetaMethod::new_static(METH_SANITIZE_LOG, 0, AccessLevel::Developer, "Null", "String", &[], ""),
227228
];
228-
match method.as_ref() {
229-
METH_DIR => return Ok(ResolvedRequest::dir(METHODS)),
230-
METH_LS => return Ok(ResolvedRequest::ls(METHODS, async || {
229+
match method {
230+
Method::Dir(dir) => return dir.resolve(METHODS),
231+
Method::Ls(ls) => return ls.resolve(METHODS, async || {
231232
journaldir_ls_handler(get_journaldir_entries(path).await?).await
232-
})),
233-
METH_LS_FILES => return Ok(ResolvedRequest::method(METHODS, method, async || {
234-
journaldir_lsfiles_handler(get_journaldir_entries(path).await?).await
235-
})),
236-
METH_LOG_SIZE_LIMIT => return Ok(ResolvedRequest::method(METHODS, method, async move || Ok(log_size_limit(&app_state.config)))),
237-
METH_TOTAL_LOG_SIZE => return Ok(ResolvedRequest::method(METHODS, method, async move || total_log_size(&app_state.config)
233+
}),
234+
Method::Other(m) => match m.method() {
235+
METH_LS_FILES => return m.resolve(METHODS, async || {
236+
journaldir_lsfiles_handler(get_journaldir_entries(path).await?).await
237+
}),
238+
METH_LOG_SIZE_LIMIT => return m.resolve(METHODS, async move || Ok(log_size_limit(&app_state.config))),
239+
METH_TOTAL_LOG_SIZE => return m.resolve(METHODS, async move || total_log_size(&app_state.config)
238240
.await
239241
.map(RpcValue::from)
240-
.map_err(rpc_error_filesystem))
241-
),
242-
METH_LOG_USAGE => return Ok(ResolvedRequest::method(METHODS, method, async move || total_log_size(&app_state.config)
243-
.await
244-
.map(|size| 100. * (size as f64) / (log_size_limit(&app_state.config) as f64))
245-
.map_err(rpc_error_filesystem))
246-
),
247-
METH_SYNC_LOG => return Ok(ResolvedRequest::method(METHODS, method, async move || sync_log_request_handler(&param, app_state).await)),
248-
METH_SYNC_INFO => return Ok(ResolvedRequest::method(METHODS, method, async move || Ok((*app_state.sync_info.sites_sync_info.read().await).to_owned()))),
249-
METH_SANITIZE_LOG => return Ok(ResolvedRequest::method(METHODS, method, async move || app_state.sync_cmd_tx
250-
.send(crate::sync::SyncCommand::Cleanup)
251-
.map(|_| true)
252-
.map_err(|_| RpcError::new(RpcErrorCode::InternalError, "Cannot send the command through the channel"))
253-
)),
254-
_ => return Err(rpc_error_method_not_found()),
242+
.map_err(rpc_error_filesystem)
243+
),
244+
METH_LOG_USAGE => return m.resolve(METHODS, async move || total_log_size(&app_state.config)
245+
.await
246+
.map(|size| 100. * (size as f64) / (log_size_limit(&app_state.config) as f64))
247+
.map_err(rpc_error_filesystem)
248+
),
249+
METH_SYNC_LOG => return m.resolve(METHODS, async move || sync_log_request_handler(&param, app_state).await),
250+
METH_SYNC_INFO => return m.resolve(METHODS, async move || Ok((*app_state.sync_info.sites_sync_info.read().await).to_owned())),
251+
METH_SANITIZE_LOG => return m.resolve(METHODS, async move || app_state.sync_cmd_tx
252+
.send(crate::sync::SyncCommand::Cleanup)
253+
.map(|_| true)
254+
.map_err(|_| RpcError::new(RpcErrorCode::InternalError, "Cannot send the command through the channel"))
255+
),
256+
_ => return err_unresolved_request(),
257+
}
255258
}
256259
}
257260

258261
// Probe the path on the fs. Prevent leaking FS info to unauthorized
259262
// users, do not return `rpc_error_filesystem`.
260263
let path_meta = tokio::fs::metadata(&path)
261264
.await
262-
.map_err(|_| rpc_error_unknown_method_on_path(&path, &method))?;
265+
.inspect_err(|err| error!("Cannot read FS metadata, path: {path}, error: {err}"))
266+
.or(Err(UnresolvedRequest))?;
263267

264268
if path_meta.is_dir() {
265269
const METHODS: &[MetaMethod] = &[META_METHOD_LS_FILES];
266-
match method.as_str() {
267-
METH_DIR => Ok(ResolvedRequest::dir(METHODS)),
268-
METH_LS => Ok(ResolvedRequest::ls(METHODS, async || {
270+
match method {
271+
Method::Dir(dir) => dir.resolve(METHODS),
272+
Method::Ls(ls) => ls.resolve(METHODS, async || {
269273
journaldir_ls_handler(get_journaldir_entries(path).await?).await
270-
})),
271-
METH_LS_FILES => Ok(ResolvedRequest::method(METHODS, method, async || {
274+
}),
275+
Method::Other(m) if m.method() == METH_LS_FILES => m.resolve(METHODS, async || {
272276
journaldir_lsfiles_handler(get_journaldir_entries(path).await?).await
273-
})),
274-
_ => Err(rpc_error_method_not_found()),
277+
}),
278+
_ => err_unresolved_request(),
275279
}
276280
} else if path_meta.is_file() {
277281
const METHODS: &[MetaMethod] = &[
@@ -280,15 +284,18 @@ async fn shvjournal_request_handler(
280284
MetaMethod::new_static(METH_READ, 0, AccessLevel::Read, "Map", "Blob", &[], "Parameters\n offset: file offset to start read, default is 0\n size: number of bytes to read starting on offset, default is till end of file\n"),
281285
MetaMethod::new_static(METH_READ_COMPRESSED, 0, AccessLevel::Read, "Map", "Blob", &[], "Parameters\n read() parameters\n compressionType: gzip (default) | qcompress"),
282286
];
283-
match method.as_str() {
284-
METH_DIR => Ok(ResolvedRequest::dir(METHODS)),
285-
METH_LS => Ok(ResolvedRequest::ls(METHODS, ls_empty)),
286-
_ => Ok(ResolvedRequest::method(METHODS, method.clone(), async move || {
287-
journalfile_methods_handler(&method, &path, path_meta.len(), &param).await
288-
})),
287+
match method {
288+
Method::Dir(dir) => dir.resolve(METHODS),
289+
Method::Ls(ls) => ls.resolve(METHODS, ls_empty),
290+
Method::Other(m) => {
291+
let method = m.method().to_owned();
292+
m.resolve(METHODS, async move || {
293+
journalfile_methods_handler(&method, &path, path_meta.len(), &param).await
294+
})
295+
},
289296
}
290297
} else {
291-
Err(rpc_error_unknown_method_on_path(path, method))
298+
err_unresolved_request()
292299
}
293300
}
294301

@@ -562,20 +569,20 @@ pub(crate) async fn request_handler(
562569
rq: RpcMessage,
563570
client_cmd_tx: ClientCommandSender,
564571
app_state: Arc<State>,
565-
) -> Result<ResolvedRequest, RpcError>
572+
) -> RequestHandlerResult
566573
{
567574
let path = rq.shv_path().map_or_else(String::new, String::from);
568-
let method = rq.method().map_or_else(String::new, String::from);
575+
let method = Method::from_request(&rq);
569576
let param = rq.param().map_or_else(RpcValue::null, RpcValue::clone);
570577

571578
match NodeType::from_path(&path) {
572579
NodeType::DotApp => {
573-
match method.as_str() {
574-
METH_DIR => Ok(ResolvedRequest::dir(DOT_APP_METHODS)),
575-
METH_LS => Ok(ResolvedRequest::ls(DOT_APP_METHODS, ls_empty)),
576-
_ => Ok(ResolvedRequest::method_opt(DOT_APP_METHODS, method, async move ||
577-
DOT_APP_NODE.process_request(rq, client_cmd_tx).await
578-
)),
580+
match method {
581+
Method::Dir(dir) => dir.resolve(DOT_APP_METHODS),
582+
Method::Ls(ls) => ls.resolve(DOT_APP_METHODS, ls_empty),
583+
Method::Other(m) => m.resolve_opt(DOT_APP_METHODS, async move ||
584+
DOT_APP_NODE.process_request(rq, client_cmd_tx).await
585+
),
579586
}
580587
}
581588
NodeType::ShvJournal =>
@@ -585,11 +592,11 @@ pub(crate) async fn request_handler(
585592
MetaMethod::new_static(METH_GET, 0, AccessLevel::Developer, "String", "RpcValue", &[], ""),
586593
MetaMethod::new_static(METH_GET_CACHE, 0, AccessLevel::Developer, "Null", "Map", &[], ""),
587594
];
588-
match method.as_str() {
589-
METH_DIR => Ok(ResolvedRequest::dir(METHODS)),
590-
METH_LS => Ok(ResolvedRequest::ls(METHODS, ls_empty)),
591-
METH_GET | METH_GET_CACHE => Ok(ResolvedRequest::method(METHODS, method, async || Err::<(), _>(rpc_error_not_implemented()))),
592-
_ => Err(rpc_error_method_not_found()),
595+
match method {
596+
Method::Dir(dir) => dir.resolve(METHODS),
597+
Method::Ls(ls) => ls.resolve(METHODS, ls_empty),
598+
Method::Other(m) if m.method() == METH_GET || m.method() == METH_GET_CACHE => m.resolve(METHODS, async || Err::<(), _>(rpc_error_not_implemented())),
599+
_ => err_unresolved_request(),
593600
}
594601
}
595602
NodeType::Root => {
@@ -607,9 +614,9 @@ pub(crate) async fn request_handler(
607614
// shvGitCommit
608615
// reloadSites (wr) -> Bool
609616
];
610-
match method.as_str() {
611-
METH_DIR => Ok(ResolvedRequest::dir(METHODS)),
612-
METH_LS => {
617+
match method {
618+
Method::Dir(dir) => dir.resolve(METHODS),
619+
Method::Ls(ls) => {
613620
let ls_handler = async move || {
614621
let mut nodes = vec![
615622
".app".to_string(),
@@ -620,25 +627,27 @@ pub(crate) async fn request_handler(
620627
nodes.append(&mut children_on_path(&app_state.sites_data.read().await.sites_info, ROOT_PATH).unwrap_or_default());
621628
Ok(nodes)
622629
};
623-
Ok(ResolvedRequest::ls(METHODS, ls_handler))
630+
ls.resolve(METHODS, ls_handler)
631+
}
632+
Method::Other(m) => match m.method() {
633+
METH_VERSION => m.resolve(METHODS, async || Ok(env!("CARGO_PKG_VERSION"))),
634+
METH_UPTIME => m.resolve(METHODS, async move || {
635+
Ok(humantime::format_duration(std::time::Duration::from_secs(app_state.start_time.elapsed().as_secs())).to_string())
636+
}),
637+
METH_ALARM_LOG => m.resolve(METHODS, async move || {
638+
let params = AlarmLogParams::try_from(param)
639+
.map_err(|err| RpcError::new(RpcErrorCode::InvalidParam, format!("Wrong alarmLog parameters: {err}")))?;
640+
Ok(alarmlog_impl(&path, &params, app_state).await)
641+
}),
642+
_ => err_unresolved_request(),
624643
}
625-
METH_VERSION => Ok(ResolvedRequest::method(METHODS, method, async || Ok(env!("CARGO_PKG_VERSION")))),
626-
METH_UPTIME => Ok(ResolvedRequest::method(METHODS, method, async move || {
627-
Ok(humantime::format_duration(std::time::Duration::from_secs(app_state.start_time.elapsed().as_secs())).to_string())
628-
})),
629-
METH_ALARM_LOG => Ok(ResolvedRequest::method(METHODS, method, async move || {
630-
let params = AlarmLogParams::try_from(param)
631-
.map_err(|err| RpcError::new(RpcErrorCode::InvalidParam, format!("Wrong alarmLog parameters: {err}")))?;
632-
Ok(alarmlog_impl(&path, &params, app_state).await)
633-
})),
634-
_ => Err(rpc_error_method_not_found()),
635644
}
636645
}
637646
NodeType::History => {
638647
let methods = {
639648
let sites_data = app_state.sites_data.read().await;
640649
let is_site_path = children_on_path(&sites_data.sites_info, &path)
641-
.ok_or_else(|| rpc_error_unknown_method_on_path(&path, &method))?
650+
.ok_or(UnresolvedRequest)?
642651
.is_empty();
643652
if is_site_path {
644653
let is_pushlog = sites_data.sites_info.get(&path)
@@ -661,26 +670,23 @@ pub(crate) async fn request_handler(
661670
METHODS
662671
}
663672
};
664-
match method.as_str() {
665-
METH_DIR => Ok(ResolvedRequest::dir(methods)),
666-
METH_LS => {
667-
Ok(ResolvedRequest::ls(methods, async move || {
673+
match method {
674+
Method::Dir(dir) => dir.resolve(methods),
675+
Method::Ls(ls) => {
676+
ls.resolve(methods, async move || {
668677
let children = children_on_path(&app_state.sites_data.read().await.sites_info, &path)
669678
.unwrap_or_else(|| panic!("Children on path `{path}` should be Some after methods processing"));
670679
Ok(children)
671-
}))
680+
})
672681
}
673-
_ => Ok(ResolvedRequest::method(methods, method.clone(), async move || {
674-
match method.as_str() {
675-
METH_GET_LOG => getlog_handler_rq(&path, &param, app_state).await,
676-
METH_ALARM_TABLE => alarmtable_handler::<CommonAlarm>(&path, app_state).await,
677-
METH_STATE_ALARM_TABLE => alarmtable_handler::<StateAlarm>(&path, app_state).await,
678-
METH_ALARM_LOG => alarmlog_handler(&path, &param, app_state).await,
679-
METH_PUSH_LOG => pushlog_handler(&path, param, app_state).await,
680-
_ => Err(rpc_error_method_not_found()),
681-
}
682-
683-
})),
682+
Method::Other(m) => match m.method() {
683+
METH_GET_LOG => m.resolve(methods, async move || { getlog_handler_rq(&path, &param, app_state).await }),
684+
METH_ALARM_TABLE => m.resolve(methods, async move || { alarmtable_handler::<CommonAlarm>(&path, app_state).await }),
685+
METH_STATE_ALARM_TABLE => m.resolve(methods, async move || { alarmtable_handler::<StateAlarm>(&path, app_state).await }),
686+
METH_ALARM_LOG => m.resolve(methods, async move || { alarmlog_handler(&path, &param, app_state).await }),
687+
METH_PUSH_LOG => m.resolve(methods, async move || { pushlog_handler(&path, param, app_state).await }),
688+
_ => err_unresolved_request(),
689+
},
684690
}
685691
}
686692
}

0 commit comments

Comments
 (0)