Skip to main content

futu_backend/
connection_lifecycle_runtime.rs

1//! Production transport adapter for connection lifecycle commands.
2//!
3//! Login, session-key refresh, and WebTCP-short requests are stateful channel
4//! primitives rather than ordinary business commands. They share command-spec
5//! identity and execution reporting, but use `ConnectionLifecycleRuntime` so
6//! ordinary `CommandRuntime` cannot accidentally own reconnect/auth behavior.
7
8use async_trait::async_trait;
9use bytes::Bytes;
10use futu_command_runtime::{
11    CommandExecutionContext, CommandRequest, CommandResponse, CommandTransport,
12    ConnectionLifecycleRuntime,
13};
14use futu_command_spec::{BackendChannelKind, CommandSpecId, command_spec_by_id};
15use futu_core::error::{FutuError, Result};
16
17use crate::conn::BackendConn;
18
19struct ConnectionLifecycleBackendTransport<'a> {
20    conn: &'a BackendConn,
21    channel: BackendChannelKind,
22}
23
24#[async_trait]
25impl CommandTransport for ConnectionLifecycleBackendTransport<'_> {
26    fn channel_kind(&self) -> Option<BackendChannelKind> {
27        Some(self.channel)
28    }
29
30    async fn execute(&self, request: CommandRequest) -> Result<CommandResponse> {
31        let frame = self
32            .conn
33            .request_with_reserved(request.cmd_id, request.body.to_vec(), request.reserved)
34            .await?;
35        Ok(CommandResponse {
36            request_serial_no: frame.header.serial_no,
37            cmd_id: frame.header.cmd_id,
38            body: frame.body,
39            ex_head: frame.ex_head,
40        })
41    }
42}
43
44pub async fn execute_connection_lifecycle(
45    conn: &BackendConn,
46    spec_id: CommandSpecId,
47    body: Vec<u8>,
48) -> Result<CommandResponse> {
49    let spec = command_spec_by_id(spec_id)
50        .ok_or_else(|| FutuError::Codec(format!("unknown lifecycle command spec: {spec_id:?}")))?;
51    let runtime = ConnectionLifecycleRuntime::new(ConnectionLifecycleBackendTransport {
52        conn,
53        channel: spec.channel,
54    });
55    let execution = runtime
56        .execute(CommandExecutionContext::new(spec_id, Bytes::from(body)))
57        .await
58        .map_err(|error| FutuError::Codec(format!("connection lifecycle spec error: {error}")))?;
59    let report = &execution.report;
60    tracing::trace!(
61        command = report.command_name,
62        cmd_id = report.cmd_id,
63        channel = ?report.channel,
64        outcome = ?report.outcome,
65        response_body_len = report.response_body_len,
66        "connection lifecycle runtime executed transport primitive"
67    );
68    execution.into_response()
69}