use serde_json::Value;
use crate::error::ImError;
pub(super) fn path_seg<'a>(args: &'a Value, key: &str, cmd: &str) -> Result<&'a str, ImError> {
let v = args
.get(key)
.and_then(Value::as_str)
.filter(|s| !s.is_empty() && !s.contains('/'))
.ok_or_else(|| ImError::Parse(format!("{cmd}: 缺/坏 path 段 '{key}'(非空且不含 '/')")))?;
Ok(v)
}
macro_rules! __outbound_impl {
(static $cmd:ident, $reg:ident, $name:literal, $auth:ident, $is_read:literal, $build:expr) => {
struct $cmd;
impl $crate::outbound::registry::OutboundCommand for $cmd {
fn name(&self) -> &'static str { $name }
fn build(&self, a: &::serde_json::Value)
-> ::std::result::Result<(&'static str, ::serde_json::Value), $crate::error::ImError> {
let f: fn(&::serde_json::Value, &'static str)
-> ::std::result::Result<(&'static str, ::serde_json::Value), $crate::error::ImError> = $build;
f(a, $name)
}
fn auth_kind(&self) -> ::helix_core::AuthKind { ::helix_core::AuthKind::$auth }
fn is_read(&self) -> bool { $is_read }
}
__outbound_impl!(@register $cmd, $reg, $name);
};
(dyn $cmd:ident, $reg:ident, $name:literal, $base:literal, $auth:ident, $is_read:literal, $path:expr, $body:expr) => {
struct $cmd;
impl $crate::outbound::registry::OutboundCommand for $cmd {
fn name(&self) -> &'static str { $name }
fn build(&self, a: &::serde_json::Value)
-> ::std::result::Result<(&'static str, ::serde_json::Value), $crate::error::ImError> {
let f: fn(&::serde_json::Value, &'static str)
-> ::std::result::Result<::serde_json::Value, $crate::error::ImError> = $body;
Ok(($base, f(a, $name)?))
}
fn path_override(&self, a: &::serde_json::Value)
-> ::std::option::Option<::std::result::Result<::std::string::String, $crate::error::ImError>> {
let f: fn(&::serde_json::Value, &'static str)
-> ::std::result::Result<::std::string::String, $crate::error::ImError> = $path;
::std::option::Option::Some(f(a, $name))
}
fn auth_kind(&self) -> ::helix_core::AuthKind { ::helix_core::AuthKind::$auth }
fn is_read(&self) -> bool { $is_read }
}
__outbound_impl!(@register $cmd, $reg, $name);
};
(@register $cmd:ident, $reg:ident, $name:literal) => {
::inventory::submit! {
$crate::outbound::registry::OutboundRegistration {
name: $name,
command: &$cmd,
}
}
};
}
macro_rules! bot_cmd {
(read $cmd:ident, $reg:ident, $name:literal, $build:expr) => {
__outbound_impl!(static $cmd, $reg, $name, Session, true, $build);
};
($cmd:ident, $reg:ident, $name:literal, $build:expr) => {
__outbound_impl!(static $cmd, $reg, $name, Session, false, $build);
};
}
macro_rules! bot_call_cmd {
(read $cmd:ident, $reg:ident, $name:literal, $build:expr) => {
__outbound_impl!(static $cmd, $reg, $name, Bot, true, $build);
};
($cmd:ident, $reg:ident, $name:literal, $build:expr) => {
__outbound_impl!(static $cmd, $reg, $name, Bot, false, $build);
};
}
macro_rules! bot_dyn_cmd {
(read $cmd:ident, $reg:ident, $name:literal, $base:literal, $path:expr, $body:expr) => {
__outbound_impl!(dyn $cmd, $reg, $name, $base, Session, true, $path, $body);
};
($cmd:ident, $reg:ident, $name:literal, $base:literal, $path:expr, $body:expr) => {
__outbound_impl!(dyn $cmd, $reg, $name, $base, Session, false, $path, $body);
};
}
macro_rules! bot_call_dyn_cmd {
(read $cmd:ident, $reg:ident, $name:literal, $base:literal, $path:expr, $body:expr) => {
__outbound_impl!(dyn $cmd, $reg, $name, $base, Bot, true, $path, $body);
};
($cmd:ident, $reg:ident, $name:literal, $base:literal, $path:expr, $body:expr) => {
__outbound_impl!(dyn $cmd, $reg, $name, $base, Bot, false, $path, $body);
};
}
pub(super) mod agents;
pub(super) mod bot_manage;
pub(super) mod calls;
pub(super) mod config;
pub(super) mod gateway;
pub(super) mod webhook;
#[cfg(test)]
mod tests {
use crate::outbound::registry::is_read;
#[test]
fn bot_agent_read_commands_marked_read() {
for name in [
"im_webhook_config_get", "im_bot_agent_config_get", "im_bot_agent_configs_enabled", "im_bot_agent_channel_bots", "im_bot_agent_channel_available_bots", "im_bot_agent_info", "im_bot_agent_team_members", "im_bot_agent_get_user", "im_bot_agent_team_channel", "im_bot_list_visible", "im_bot_token_list", "im_bot_visible_user_list", ] {
assert!(is_read(name), "{name} 应标 is_read==true(读族)");
}
}
#[test]
fn bot_agent_write_commands_not_read() {
for name in [
"im_create_bot", "im_bot_callback", "im_bot_create", "im_bot_token_generate", "im_bot_token_revoke", "im_bot_agent_send_direct", "im_agent_callback", ] {
assert!(!is_read(name), "{name} 应保持 is_read==false(写族)");
}
}
}