feat: add session event polling, channel/client list, and text messages
- Add SessionEvent enum with Connected, ChannelList, ClientList, ServerInfo, TextMessage, ClientEntered, ClientLeft, ClientMoved - Add poll_events, request_channel_list, request_client_list Tauri commands - Session now parses initserver, channellist, clientlist, notify* commands and emits events - Frontend polls events every 500ms when connected - Frontend displays server info, channel list, client list, and text messages
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
interface Identity {
|
interface Identity {
|
||||||
@@ -18,6 +18,51 @@ interface Bookmark {
|
|||||||
last_connected: string | null;
|
last_connected: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ChannelEntry {
|
||||||
|
cid: number;
|
||||||
|
pid: number;
|
||||||
|
channel_order: number;
|
||||||
|
channel_name: string;
|
||||||
|
total_clients: number;
|
||||||
|
channel_needed_subscribe_power: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClientEntry {
|
||||||
|
clid: number;
|
||||||
|
cid: number;
|
||||||
|
client_database_id: number;
|
||||||
|
client_nickname: string;
|
||||||
|
client_type: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServerInfo {
|
||||||
|
name: string;
|
||||||
|
platform: string;
|
||||||
|
version: string;
|
||||||
|
max_clients: number;
|
||||||
|
clients_online: number;
|
||||||
|
channels_online: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextMessage {
|
||||||
|
invoker_id: number;
|
||||||
|
invoker_name: string;
|
||||||
|
message: string;
|
||||||
|
target_mode: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionEvent =
|
||||||
|
| { Connected: { client_id: number } }
|
||||||
|
| { ChannelList: ChannelEntry[] }
|
||||||
|
| { ClientList: ClientEntry[] }
|
||||||
|
| { ServerInfo: ServerInfo }
|
||||||
|
| { TextMessage: TextMessage }
|
||||||
|
| { ClientEntered: { clid: number; cid: number; client_nickname: string } }
|
||||||
|
| { ClientLeft: { clid: number; reason: string } }
|
||||||
|
| { ClientMoved: { clid: number; cid: number } }
|
||||||
|
| { Error: string }
|
||||||
|
| { Disconnected: null };
|
||||||
|
|
||||||
interface ServerQueryChannel {
|
interface ServerQueryChannel {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -52,10 +97,15 @@ function App() {
|
|||||||
const [nickname, setNickname] = useState('');
|
const [nickname, setNickname] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [connected, setConnected] = useState(false);
|
const [connected, setConnected] = useState(false);
|
||||||
|
const [serverInfo, setServerInfo] = useState<ServerInfo | null>(null);
|
||||||
|
const [channels, setChannels] = useState<ChannelEntry[]>([]);
|
||||||
|
const [clients, setClients] = useState<ClientEntry[]>([]);
|
||||||
|
const [messages, setMessages] = useState<TextMessage[]>([]);
|
||||||
const [queryPort, setQueryPort] = useState(10011);
|
const [queryPort, setQueryPort] = useState(10011);
|
||||||
const [querySnapshot, setQuerySnapshot] = useState<ServerQuerySnapshot | null>(null);
|
const [querySnapshot, setQuerySnapshot] = useState<ServerQuerySnapshot | null>(null);
|
||||||
const [queryLoading, setQueryLoading] = useState(false);
|
const [queryLoading, setQueryLoading] = useState(false);
|
||||||
const [queryError, setQueryError] = useState<string | null>(null);
|
const [queryError, setQueryError] = useState<string | null>(null);
|
||||||
|
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadIdentities();
|
loadIdentities();
|
||||||
@@ -67,6 +117,56 @@ function App() {
|
|||||||
setQueryError(null);
|
setQueryError(null);
|
||||||
}, [selectedBookmark]);
|
}, [selectedBookmark]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (connected) {
|
||||||
|
pollEvents();
|
||||||
|
pollRef.current = setInterval(pollEvents, 500);
|
||||||
|
return () => {
|
||||||
|
if (pollRef.current) clearInterval(pollRef.current);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (pollRef.current) clearInterval(pollRef.current);
|
||||||
|
}
|
||||||
|
}, [connected]);
|
||||||
|
|
||||||
|
async function pollEvents() {
|
||||||
|
try {
|
||||||
|
const events = await invoke<SessionEvent[]>('poll_events');
|
||||||
|
for (const event of events) {
|
||||||
|
handleEvent(event);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to poll events:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEvent(event: SessionEvent) {
|
||||||
|
if ('Connected' in event) {
|
||||||
|
console.log('Connected as client', event.Connected.client_id);
|
||||||
|
} else if ('ServerInfo' in event) {
|
||||||
|
setServerInfo(event.ServerInfo);
|
||||||
|
} else if ('ChannelList' in event) {
|
||||||
|
setChannels(event.ChannelList);
|
||||||
|
} else if ('ClientList' in event) {
|
||||||
|
setClients(event.ClientList);
|
||||||
|
} else if ('TextMessage' in event) {
|
||||||
|
setMessages((prev) => [...prev, event.TextMessage]);
|
||||||
|
} else if ('ClientEntered' in event) {
|
||||||
|
console.log('Client entered:', event.ClientEntered);
|
||||||
|
} else if ('ClientLeft' in event) {
|
||||||
|
console.log('Client left:', event.ClientLeft);
|
||||||
|
} else if ('ClientMoved' in event) {
|
||||||
|
console.log('Client moved:', event.ClientMoved);
|
||||||
|
} else if ('Error' in event) {
|
||||||
|
console.error('Session error:', event.Error);
|
||||||
|
} else if ('Disconnected' in event) {
|
||||||
|
setConnected(false);
|
||||||
|
setServerInfo(null);
|
||||||
|
setChannels([]);
|
||||||
|
setClients([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadIdentities() {
|
async function loadIdentities() {
|
||||||
try {
|
try {
|
||||||
const result = await invoke<Identity[]>('get_identities');
|
const result = await invoke<Identity[]>('get_identities');
|
||||||
@@ -96,6 +196,7 @@ function App() {
|
|||||||
password: password || null,
|
password: password || null,
|
||||||
});
|
});
|
||||||
setConnected(true);
|
setConnected(true);
|
||||||
|
setMessages([]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to connect:', error);
|
console.error('Failed to connect:', error);
|
||||||
}
|
}
|
||||||
@@ -105,6 +206,9 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
await invoke('disconnect');
|
await invoke('disconnect');
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
|
setServerInfo(null);
|
||||||
|
setChannels([]);
|
||||||
|
setClients([]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to disconnect:', error);
|
console.error('Failed to disconnect:', error);
|
||||||
}
|
}
|
||||||
@@ -141,7 +245,9 @@ function App() {
|
|||||||
<h1>ReTeamSpeak</h1>
|
<h1>ReTeamSpeak</h1>
|
||||||
<div className="connection-status">
|
<div className="connection-status">
|
||||||
{connected ? (
|
{connected ? (
|
||||||
<span className="status connected">已连接</span>
|
<span className="status connected">
|
||||||
|
已连接 {serverInfo ? `— ${serverInfo.name}` : ''}
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="status disconnected">未连接</span>
|
<span className="status disconnected">未连接</span>
|
||||||
)}
|
)}
|
||||||
@@ -212,6 +318,60 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{connected && (
|
||||||
|
<section className="session-panel">
|
||||||
|
<div className="query-grid">
|
||||||
|
<div className="query-card">
|
||||||
|
<h3>服务器</h3>
|
||||||
|
{serverInfo ? (
|
||||||
|
<>
|
||||||
|
<p><strong>{serverInfo.name}</strong></p>
|
||||||
|
<p>{serverInfo.platform} / {serverInfo.version}</p>
|
||||||
|
<p>{serverInfo.clients_online} / {serverInfo.max_clients} 在线</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p>等待服务器信息...</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="query-card">
|
||||||
|
<h3>频道 ({channels.length})</h3>
|
||||||
|
<ul className="query-list">
|
||||||
|
{channels.map((ch) => (
|
||||||
|
<li key={ch.cid}>
|
||||||
|
<span>{ch.channel_name}</span>
|
||||||
|
<small>{ch.total_clients} 人</small>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="query-card">
|
||||||
|
<h3>客户端 ({clients.length})</h3>
|
||||||
|
<ul className="query-list">
|
||||||
|
{clients.map((c) => (
|
||||||
|
<li key={c.clid}>
|
||||||
|
<span>{c.client_nickname}</span>
|
||||||
|
<small>#{c.clid}</small>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="messages-panel">
|
||||||
|
<h3>消息 ({messages.length})</h3>
|
||||||
|
<ul className="message-list">
|
||||||
|
{messages.map((msg, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
<strong>{msg.invoker_name}:</strong> {msg.message}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
<section className="query-panel">
|
<section className="query-panel">
|
||||||
<div className="query-header">
|
<div className="query-header">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::net::SocketAddr;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
use tokio::net::lookup_host;
|
use tokio::net::lookup_host;
|
||||||
use tscore::{ClientConfig, IdentityKey, QueryClient, Session};
|
use tscore::{ClientConfig, IdentityKey, QueryClient, Session, SessionEvent};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
@@ -273,6 +273,37 @@ pub async fn send_raw_command(state: State<'_, AppState>, command: String) -> Re
|
|||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn poll_events(state: State<'_, AppState>) -> Result<Vec<SessionEvent>, String> {
|
||||||
|
let mut session_guard = state.session_handle.lock().await;
|
||||||
|
let handle = session_guard.as_mut().ok_or("not connected")?;
|
||||||
|
let mut events = Vec::new();
|
||||||
|
while let Some(event) = handle.try_recv_event() {
|
||||||
|
events.push(event);
|
||||||
|
}
|
||||||
|
Ok(events)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn request_channel_list(state: State<'_, AppState>) -> Result<(), String> {
|
||||||
|
let session_guard = state.session_handle.lock().await;
|
||||||
|
let handle = session_guard.as_ref().ok_or("not connected")?;
|
||||||
|
handle
|
||||||
|
.request_channel_list()
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn request_client_list(state: State<'_, AppState>) -> Result<(), String> {
|
||||||
|
let session_guard = state.session_handle.lock().await;
|
||||||
|
let handle = session_guard.as_ref().ok_or("not connected")?;
|
||||||
|
handle
|
||||||
|
.request_client_list()
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_messages(
|
pub async fn get_messages(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ pub fn run() {
|
|||||||
commands::send_server_message,
|
commands::send_server_message,
|
||||||
commands::send_private_message,
|
commands::send_private_message,
|
||||||
commands::send_raw_command,
|
commands::send_raw_command,
|
||||||
|
commands::poll_events,
|
||||||
|
commands::request_channel_list,
|
||||||
|
commands::request_client_list,
|
||||||
commands::get_messages,
|
commands::get_messages,
|
||||||
commands::server_query_snapshot,
|
commands::server_query_snapshot,
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -283,13 +283,34 @@ impl Session {
|
|||||||
fn collect_init_event(cmd: &Command, events: &mut Vec<SessionEvent>) {
|
fn collect_init_event(cmd: &Command, events: &mut Vec<SessionEvent>) {
|
||||||
match cmd.name.as_str() {
|
match cmd.name.as_str() {
|
||||||
"initserver" => {
|
"initserver" => {
|
||||||
let client_id = cmd.get("client_id").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let client_id = cmd
|
||||||
let name = cmd.get("virtualserver_name").unwrap_or_default().to_string();
|
.get("client_id")
|
||||||
let platform = cmd.get("virtualserver_platform").unwrap_or_default().to_string();
|
.and_then(|v| v.parse().ok())
|
||||||
let version = cmd.get("virtualserver_version").unwrap_or_default().to_string();
|
.unwrap_or(0);
|
||||||
let max_clients = cmd.get("virtualserver_maxclients").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let name = cmd
|
||||||
let clients_online = cmd.get("virtualserver_clientsonline").and_then(|v| v.parse().ok()).unwrap_or(0);
|
.get("virtualserver_name")
|
||||||
let channels_online = cmd.get("virtualserver_channelsonline").and_then(|v| v.parse().ok()).unwrap_or(0);
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let platform = cmd
|
||||||
|
.get("virtualserver_platform")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let version = cmd
|
||||||
|
.get("virtualserver_version")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let max_clients = cmd
|
||||||
|
.get("virtualserver_maxclients")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let clients_online = cmd
|
||||||
|
.get("virtualserver_clientsonline")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let channels_online = cmd
|
||||||
|
.get("virtualserver_channelsonline")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
events.push(SessionEvent::ServerInfo {
|
events.push(SessionEvent::ServerInfo {
|
||||||
name,
|
name,
|
||||||
@@ -313,10 +334,19 @@ impl Session {
|
|||||||
let mut channels = Vec::new();
|
let mut channels = Vec::new();
|
||||||
let cid = cmd.get("cid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let cid = cmd.get("cid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||||
let pid = cmd.get("pid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let pid = cmd.get("pid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||||
let channel_order = cmd.get("channel_order").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let channel_order = cmd
|
||||||
|
.get("channel_order")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
let channel_name = cmd.get("channel_name").unwrap_or_default().to_string();
|
let channel_name = cmd.get("channel_name").unwrap_or_default().to_string();
|
||||||
let total_clients = cmd.get("total_clients").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let total_clients = cmd
|
||||||
let channel_needed_subscribe_power = cmd.get("channel_needed_subscribe_power").and_then(|v| v.parse().ok()).unwrap_or(0);
|
.get("total_clients")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let channel_needed_subscribe_power = cmd
|
||||||
|
.get("channel_needed_subscribe_power")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
channels.push(ChannelEntry {
|
channels.push(ChannelEntry {
|
||||||
cid,
|
cid,
|
||||||
@@ -392,22 +422,46 @@ impl Session {
|
|||||||
async fn emit_command_event(&self, cmd: &Command) {
|
async fn emit_command_event(&self, cmd: &Command) {
|
||||||
let event = match cmd.name.as_str() {
|
let event = match cmd.name.as_str() {
|
||||||
"initserver" => {
|
"initserver" => {
|
||||||
let client_id = cmd.get("client_id").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let client_id = cmd
|
||||||
let name = cmd.get("virtualserver_name").unwrap_or_default().to_string();
|
.get("client_id")
|
||||||
let platform = cmd.get("virtualserver_platform").unwrap_or_default().to_string();
|
.and_then(|v| v.parse().ok())
|
||||||
let version = cmd.get("virtualserver_version").unwrap_or_default().to_string();
|
.unwrap_or(0);
|
||||||
let max_clients = cmd.get("virtualserver_maxclients").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let name = cmd
|
||||||
let clients_online = cmd.get("virtualserver_clientsonline").and_then(|v| v.parse().ok()).unwrap_or(0);
|
.get("virtualserver_name")
|
||||||
let channels_online = cmd.get("virtualserver_channelsonline").and_then(|v| v.parse().ok()).unwrap_or(0);
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let platform = cmd
|
||||||
|
.get("virtualserver_platform")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let version = cmd
|
||||||
|
.get("virtualserver_version")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
let max_clients = cmd
|
||||||
|
.get("virtualserver_maxclients")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let clients_online = cmd
|
||||||
|
.get("virtualserver_clientsonline")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
let channels_online = cmd
|
||||||
|
.get("virtualserver_channelsonline")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
let _ = self.event_tx.send(SessionEvent::ServerInfo {
|
let _ = self
|
||||||
name,
|
.event_tx
|
||||||
platform,
|
.send(SessionEvent::ServerInfo {
|
||||||
version,
|
name,
|
||||||
max_clients,
|
platform,
|
||||||
clients_online,
|
version,
|
||||||
channels_online,
|
max_clients,
|
||||||
}).await;
|
clients_online,
|
||||||
|
channels_online,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
Some(SessionEvent::Connected { client_id })
|
Some(SessionEvent::Connected { client_id })
|
||||||
}
|
}
|
||||||
"channellist" => {
|
"channellist" => {
|
||||||
@@ -439,10 +493,16 @@ impl Session {
|
|||||||
Some(SessionEvent::ClientMoved { clid, cid })
|
Some(SessionEvent::ClientMoved { clid, cid })
|
||||||
}
|
}
|
||||||
"notifytextmessage" => {
|
"notifytextmessage" => {
|
||||||
let invoker_id = cmd.get("invokerid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let invoker_id = cmd
|
||||||
|
.get("invokerid")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
let invoker_name = cmd.get("invokername").unwrap_or_default().to_string();
|
let invoker_name = cmd.get("invokername").unwrap_or_default().to_string();
|
||||||
let message = cmd.get("msg").unwrap_or_default().to_string();
|
let message = cmd.get("msg").unwrap_or_default().to_string();
|
||||||
let target_mode = cmd.get("targetmode").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let target_mode = cmd
|
||||||
|
.get("targetmode")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
Some(SessionEvent::TextMessage {
|
Some(SessionEvent::TextMessage {
|
||||||
invoker_id,
|
invoker_id,
|
||||||
invoker_name,
|
invoker_name,
|
||||||
@@ -470,9 +530,15 @@ impl Session {
|
|||||||
fn parse_client_list(cmd: &Command) -> Vec<ClientEntry> {
|
fn parse_client_list(cmd: &Command) -> Vec<ClientEntry> {
|
||||||
let clid = cmd.get("clid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let clid = cmd.get("clid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||||
let cid = cmd.get("cid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let cid = cmd.get("cid").and_then(|v| v.parse().ok()).unwrap_or(0);
|
||||||
let client_database_id = cmd.get("client_database_id").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let client_database_id = cmd
|
||||||
|
.get("client_database_id")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
let client_nickname = cmd.get("client_nickname").unwrap_or_default().to_string();
|
let client_nickname = cmd.get("client_nickname").unwrap_or_default().to_string();
|
||||||
let client_type = cmd.get("client_type").and_then(|v| v.parse().ok()).unwrap_or(0);
|
let client_type = cmd
|
||||||
|
.get("client_type")
|
||||||
|
.and_then(|v| v.parse().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
vec![ClientEntry {
|
vec![ClientEntry {
|
||||||
clid,
|
clid,
|
||||||
|
|||||||
@@ -110,13 +110,17 @@ impl CommandArgument {
|
|||||||
value: None,
|
value: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CommandArgument {
|
impl fmt::Display for CommandArgument {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self.value {
|
match &self.value {
|
||||||
Some(value) => write!(f, "{}={}", escape::escape(&self.name), escape::escape(value)),
|
Some(value) => write!(
|
||||||
|
f,
|
||||||
|
"{}={}",
|
||||||
|
escape::escape(&self.name),
|
||||||
|
escape::escape(value)
|
||||||
|
),
|
||||||
None => write!(f, "{}", escape::escape(&self.name)),
|
None => write!(f, "{}", escape::escape(&self.name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +230,6 @@ impl Command {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Command {
|
impl fmt::Display for Command {
|
||||||
|
|||||||
Reference in New Issue
Block a user