338 lines
11 KiB
Rust
338 lines
11 KiB
Rust
//! Canonical RPC method catalog and lookup helpers.
|
|
#![expect(
|
|
clippy::redundant_pub_crate,
|
|
reason = "method-catalog helpers stay crate-internal while retaining explicit visibilities"
|
|
)]
|
|
|
|
#[cfg(test)]
|
|
use crate::model::RpcValue;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
/// RPC methods supported or reserved by the aria2-compatible surface.
|
|
pub enum RpcMethod {
|
|
/// Adds one or more download URIs.
|
|
Aria2AddUri,
|
|
/// Adds a torrent payload.
|
|
Aria2AddTorrent,
|
|
/// Adds a metalink payload.
|
|
Aria2AddMetalink,
|
|
/// Removes a download.
|
|
Aria2Remove,
|
|
/// Force-removes a download.
|
|
Aria2ForceRemove,
|
|
/// Pauses a download.
|
|
Aria2Pause,
|
|
/// Pauses all active downloads.
|
|
Aria2PauseAll,
|
|
/// Force-pauses a download.
|
|
Aria2ForcePause,
|
|
/// Force-pauses all active downloads.
|
|
Aria2ForcePauseAll,
|
|
/// Resumes a paused download.
|
|
Aria2Unpause,
|
|
/// Resumes all paused downloads.
|
|
Aria2UnpauseAll,
|
|
/// Returns status for a single download.
|
|
Aria2TellStatus,
|
|
/// Returns active downloads.
|
|
Aria2TellActive,
|
|
/// Returns waiting downloads.
|
|
Aria2TellWaiting,
|
|
/// Returns stopped downloads.
|
|
Aria2TellStopped,
|
|
/// Returns URIs for a download.
|
|
Aria2GetUris,
|
|
/// Returns files for a download.
|
|
Aria2GetFiles,
|
|
/// Returns peers for a `BitTorrent` download.
|
|
Aria2GetPeers,
|
|
/// Returns servers for a download.
|
|
Aria2GetServers,
|
|
/// Changes queue position for a download.
|
|
Aria2ChangePosition,
|
|
/// Changes URIs attached to a download.
|
|
Aria2ChangeUri,
|
|
/// Purges completed download results.
|
|
Aria2PurgeDownloadResult,
|
|
/// Removes one completed download result.
|
|
Aria2RemoveDownloadResult,
|
|
/// Returns aggregate global statistics.
|
|
Aria2TellGlobalStat,
|
|
/// Returns per-download options.
|
|
Aria2GetOption,
|
|
/// Changes per-download options.
|
|
Aria2ChangeOption,
|
|
/// Returns global options.
|
|
Aria2GetGlobalOption,
|
|
/// Changes global options.
|
|
Aria2ChangeGlobalOption,
|
|
/// Returns version and enabled feature metadata.
|
|
Aria2GetVersion,
|
|
/// Returns session metadata.
|
|
Aria2GetSessionInfo,
|
|
/// Persists the current session.
|
|
Aria2SaveSession,
|
|
/// Requests graceful shutdown.
|
|
Aria2Shutdown,
|
|
/// Requests forced shutdown.
|
|
Aria2ForceShutdown,
|
|
/// Executes a batch of nested method calls.
|
|
SystemMulticall,
|
|
/// Returns the method catalog.
|
|
SystemListMethods,
|
|
/// Returns the notification catalog.
|
|
SystemListNotifications,
|
|
}
|
|
|
|
impl RpcMethod {
|
|
/// Returns the canonical method name exposed on the wire.
|
|
#[must_use]
|
|
pub const fn as_str(self) -> &'static str {
|
|
match self {
|
|
Self::Aria2AddUri => "aria2.addUri",
|
|
Self::Aria2AddTorrent => "aria2.addTorrent",
|
|
Self::Aria2AddMetalink => "aria2.addMetalink",
|
|
Self::Aria2Remove => "aria2.remove",
|
|
Self::Aria2ForceRemove => "aria2.forceRemove",
|
|
Self::Aria2Pause => "aria2.pause",
|
|
Self::Aria2PauseAll => "aria2.pauseAll",
|
|
Self::Aria2ForcePause => "aria2.forcePause",
|
|
Self::Aria2ForcePauseAll => "aria2.forcePauseAll",
|
|
Self::Aria2Unpause => "aria2.unpause",
|
|
Self::Aria2UnpauseAll => "aria2.unpauseAll",
|
|
Self::Aria2TellStatus => "aria2.tellStatus",
|
|
Self::Aria2TellActive => "aria2.tellActive",
|
|
Self::Aria2TellWaiting => "aria2.tellWaiting",
|
|
Self::Aria2TellStopped => "aria2.tellStopped",
|
|
Self::Aria2GetUris => "aria2.getUris",
|
|
Self::Aria2GetFiles => "aria2.getFiles",
|
|
Self::Aria2GetPeers => "aria2.getPeers",
|
|
Self::Aria2GetServers => "aria2.getServers",
|
|
Self::Aria2ChangePosition => "aria2.changePosition",
|
|
Self::Aria2ChangeUri => "aria2.changeUri",
|
|
Self::Aria2PurgeDownloadResult => "aria2.purgeDownloadResult",
|
|
Self::Aria2RemoveDownloadResult => "aria2.removeDownloadResult",
|
|
Self::Aria2TellGlobalStat => "aria2.getGlobalStat",
|
|
Self::Aria2GetOption => "aria2.getOption",
|
|
Self::Aria2ChangeOption => "aria2.changeOption",
|
|
Self::Aria2GetGlobalOption => "aria2.getGlobalOption",
|
|
Self::Aria2ChangeGlobalOption => "aria2.changeGlobalOption",
|
|
Self::Aria2GetVersion => "aria2.getVersion",
|
|
Self::Aria2GetSessionInfo => "aria2.getSessionInfo",
|
|
Self::Aria2SaveSession => "aria2.saveSession",
|
|
Self::Aria2Shutdown => "aria2.shutdown",
|
|
Self::Aria2ForceShutdown => "aria2.forceShutdown",
|
|
Self::SystemMulticall => "system.multicall",
|
|
Self::SystemListMethods => "system.listMethods",
|
|
Self::SystemListNotifications => "system.listNotifications",
|
|
}
|
|
}
|
|
|
|
/// Returns accepted legacy aliases for compatibility handling.
|
|
#[must_use]
|
|
pub const fn legacy_aliases(self) -> &'static [&'static str] {
|
|
match self {
|
|
Self::Aria2TellGlobalStat => &["aria2.tellGlobalStat"],
|
|
Self::SystemMulticall => &["aria2.multicall"],
|
|
_ => &[],
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Canonical method ledger in the order exposed to compatibility consumers.
|
|
pub const RPC_METHOD_LEDGER: &[RpcMethod] = &[
|
|
RpcMethod::Aria2AddUri,
|
|
RpcMethod::Aria2AddTorrent,
|
|
RpcMethod::Aria2GetPeers,
|
|
RpcMethod::Aria2AddMetalink,
|
|
RpcMethod::Aria2Remove,
|
|
RpcMethod::Aria2Pause,
|
|
RpcMethod::Aria2ForcePause,
|
|
RpcMethod::Aria2PauseAll,
|
|
RpcMethod::Aria2ForcePauseAll,
|
|
RpcMethod::Aria2Unpause,
|
|
RpcMethod::Aria2UnpauseAll,
|
|
RpcMethod::Aria2ForceRemove,
|
|
RpcMethod::Aria2ChangePosition,
|
|
RpcMethod::Aria2TellStatus,
|
|
RpcMethod::Aria2GetUris,
|
|
RpcMethod::Aria2GetFiles,
|
|
RpcMethod::Aria2GetServers,
|
|
RpcMethod::Aria2TellActive,
|
|
RpcMethod::Aria2TellWaiting,
|
|
RpcMethod::Aria2TellStopped,
|
|
RpcMethod::Aria2GetOption,
|
|
RpcMethod::Aria2ChangeUri,
|
|
RpcMethod::Aria2ChangeOption,
|
|
RpcMethod::Aria2GetGlobalOption,
|
|
RpcMethod::Aria2ChangeGlobalOption,
|
|
RpcMethod::Aria2PurgeDownloadResult,
|
|
RpcMethod::Aria2RemoveDownloadResult,
|
|
RpcMethod::Aria2GetVersion,
|
|
RpcMethod::Aria2GetSessionInfo,
|
|
RpcMethod::Aria2Shutdown,
|
|
RpcMethod::Aria2ForceShutdown,
|
|
RpcMethod::Aria2TellGlobalStat,
|
|
RpcMethod::Aria2SaveSession,
|
|
RpcMethod::SystemMulticall,
|
|
RpcMethod::SystemListMethods,
|
|
RpcMethod::SystemListNotifications,
|
|
];
|
|
|
|
/// Required upstream-compatible method names that should resolve distinctly.
|
|
pub const REQUIRED_RPC_METHODS: &[&str] = &[
|
|
"aria2.addUri",
|
|
"aria2.addTorrent",
|
|
"aria2.getPeers",
|
|
"aria2.addMetalink",
|
|
"aria2.remove",
|
|
"aria2.pause",
|
|
"aria2.forcePause",
|
|
"aria2.pauseAll",
|
|
"aria2.forcePauseAll",
|
|
"aria2.unpause",
|
|
"aria2.unpauseAll",
|
|
"aria2.forceRemove",
|
|
"aria2.changePosition",
|
|
"aria2.tellStatus",
|
|
"aria2.getUris",
|
|
"aria2.getFiles",
|
|
"aria2.getServers",
|
|
"aria2.tellActive",
|
|
"aria2.tellWaiting",
|
|
"aria2.tellStopped",
|
|
"aria2.getOption",
|
|
"aria2.changeUri",
|
|
"aria2.changeOption",
|
|
"aria2.getGlobalOption",
|
|
"aria2.changeGlobalOption",
|
|
"aria2.purgeDownloadResult",
|
|
"aria2.removeDownloadResult",
|
|
"aria2.getVersion",
|
|
"aria2.getSessionInfo",
|
|
"aria2.shutdown",
|
|
"aria2.forceShutdown",
|
|
"aria2.getGlobalStat",
|
|
"aria2.saveSession",
|
|
"system.multicall",
|
|
"system.listMethods",
|
|
"system.listNotifications",
|
|
];
|
|
|
|
/// Canonical WebSocket notification names in upstream-compatible order.
|
|
const RPC_NOTIFICATION_NAMES: &[&str] = &[
|
|
"aria2.onDownloadStart",
|
|
"aria2.onDownloadPause",
|
|
"aria2.onDownloadStop",
|
|
"aria2.onDownloadComplete",
|
|
"aria2.onDownloadError",
|
|
"aria2.onBtDownloadComplete",
|
|
];
|
|
|
|
/// Returns whether a method name is part of the required compatibility set.
|
|
#[must_use]
|
|
pub fn is_required_rpc_method(method: &str) -> bool {
|
|
REQUIRED_RPC_METHODS.contains(&method)
|
|
}
|
|
|
|
/// Resolves a method name or legacy alias into the canonical method variant.
|
|
#[must_use]
|
|
pub(super) fn rpc_method(name: &str) -> Option<RpcMethod> {
|
|
RPC_METHOD_LEDGER
|
|
.iter()
|
|
.copied()
|
|
.find(|method| method.as_str() == name || method.legacy_aliases().contains(&name))
|
|
}
|
|
|
|
/// Returns the canonical RPC method names in ledger order.
|
|
#[must_use]
|
|
pub fn rpc_method_names() -> Vec<&'static str> {
|
|
RPC_METHOD_LEDGER
|
|
.iter()
|
|
.copied()
|
|
.map(RpcMethod::as_str)
|
|
.collect()
|
|
}
|
|
|
|
/// Returns the canonical WebSocket notification method names.
|
|
#[must_use]
|
|
pub(super) fn rpc_notification_names() -> Vec<&'static str> {
|
|
RPC_NOTIFICATION_NAMES.to_vec()
|
|
}
|
|
|
|
/// Builds an RPC value containing the canonical method name list.
|
|
#[must_use]
|
|
#[cfg(test)]
|
|
fn method_name_value_list() -> RpcValue {
|
|
RpcValue::Array(
|
|
rpc_method_names()
|
|
.into_iter()
|
|
.map(|m| RpcValue::String(m.to_owned()))
|
|
.collect(),
|
|
)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
/// Unit tests for the public RPC method and notification catalog.
|
|
mod tests {
|
|
use super::{
|
|
RpcMethod, method_name_value_list, rpc_method, rpc_method_names, rpc_notification_names,
|
|
};
|
|
use crate::model::RpcValue;
|
|
|
|
#[test]
|
|
/// Verifies that legacy aliases resolve internally without leaking into public catalogs.
|
|
fn legacy_aliases_resolve_but_do_not_leak_into_public_method_names() {
|
|
assert_eq!(
|
|
rpc_method("aria2.tellGlobalStat"),
|
|
Some(RpcMethod::Aria2TellGlobalStat)
|
|
);
|
|
assert_eq!(
|
|
rpc_method("aria2.multicall"),
|
|
Some(RpcMethod::SystemMulticall)
|
|
);
|
|
|
|
let method_names = rpc_method_names();
|
|
assert!(method_names.contains(&"aria2.getGlobalStat"));
|
|
assert!(method_names.contains(&"system.multicall"));
|
|
assert!(!method_names.contains(&"aria2.tellGlobalStat"));
|
|
assert!(!method_names.contains(&"aria2.multicall"));
|
|
}
|
|
|
|
#[test]
|
|
/// Verifies that public notification names keep the upstream aria2 order.
|
|
fn public_notification_names_keep_upstream_order() {
|
|
assert_eq!(
|
|
rpc_notification_names(),
|
|
vec![
|
|
"aria2.onDownloadStart",
|
|
"aria2.onDownloadPause",
|
|
"aria2.onDownloadStop",
|
|
"aria2.onDownloadComplete",
|
|
"aria2.onDownloadError",
|
|
"aria2.onBtDownloadComplete",
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Verifies that `system.listMethods` emits only public method names.
|
|
fn method_name_value_list_uses_public_names_only() {
|
|
match method_name_value_list() {
|
|
RpcValue::Array(values) => {
|
|
let names = values
|
|
.into_iter()
|
|
.map(|value| match value {
|
|
RpcValue::String(name) => name,
|
|
other => panic!("unexpected method value: {other:?}"),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
assert!(names.iter().any(|name| name == "aria2.getGlobalStat"));
|
|
assert!(!names.iter().any(|name| name == "aria2.tellGlobalStat"));
|
|
assert!(!names.iter().any(|name| name == "aria2.multicall"));
|
|
}
|
|
other => panic!("unexpected method list payload: {other:?}"),
|
|
}
|
|
}
|
|
}
|