//! 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 { 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::>(); 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:?}"), } } }