Files
aria2-rust-pro/crates/aria2-rust-pro-rpc/src/methods.rs
T

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:?}"),
}
}
}