Skip to main content

firebird_wire/wire/
response.rs

1//! Leitura genérica de resposta: `op_response` mais o vetor de status final.
2
3use crate::error::{DatabaseError, Error, Result, StatusArg, StatusVector};
4use crate::wire::consts::{arg, op};
5use crate::wire::stream::{FbStream, op_name};
6
7/// Um pacote `op_response` analisado (`P_RESP`).
8#[derive(Debug, Clone)]
9pub struct Response {
10    /// Id do objeto / handle retornado pela operação (`p_resp_object`).
11    pub handle: i32,
12    /// Id do blob (`p_resp_blob_id`), significativo apenas para operações de blob.
13    pub blob_id: u64,
14    /// Carga de dados variável (`p_resp_data`).
15    pub data: Vec<u8>,
16    /// O vetor de status; pode carregar avisos mesmo em caso de sucesso.
17    pub status: StatusVector,
18}
19
20impl Response {
21    /// Transforma um vetor de status que carrega erro em [`Error::Database`]; caso contrário
22    /// produz a resposta (os avisos são mantidos em `status`).
23    pub fn into_result(self) -> Result<Response> {
24        if self.status.is_error() {
25            return Err(Error::Database(DatabaseError::new(self.status)));
26        }
27        Ok(self)
28    }
29}
30
31/// Lê o próximo op code, pulando de forma transparente os pacotes keep-alive `op_dummy`
32/// e `op_void`.
33pub fn read_op(stream: &mut FbStream) -> Result<i32> {
34    loop {
35        let code = stream.read_i32()?;
36        if code == op::DUMMY || code == op::VOID {
37            continue;
38        }
39        return Ok(code);
40    }
41}
42
43/// Lê um vetor de status campo a campo do fluxo (stream).
44pub fn read_status_vector(stream: &mut FbStream) -> Result<StatusVector> {
45    let mut args = Vec::new();
46    let mut sql_state = None;
47
48    loop {
49        let tag = stream.read_i32()?;
50        match tag {
51            t if t == arg::END => break,
52            t if t == arg::GDS => args.push(StatusArg::Gds(stream.read_i32()?)),
53            t if t == arg::WARNING => args.push(StatusArg::Warning(stream.read_i32()?)),
54            t if t == arg::NUMBER => args.push(StatusArg::Number(stream.read_i32()?)),
55            t if t == arg::STRING || t == arg::CSTRING => {
56                let s = String::from_utf8_lossy(&stream.read_bytes()?).into_owned();
57                args.push(StatusArg::Str(s));
58            }
59            t if t == arg::INTERPRETED => {
60                let s = String::from_utf8_lossy(&stream.read_bytes()?).into_owned();
61                args.push(StatusArg::Interpreted(s));
62            }
63            t if t == arg::SQL_STATE => {
64                sql_state = Some(String::from_utf8_lossy(&stream.read_bytes()?).into_owned());
65            }
66            other => {
67                let _ = stream.read_i32()?;
68                args.push(StatusArg::Number(other));
69            }
70        }
71    }
72
73    Ok(StatusVector { args, sql_state })
74}
75
76/// Lê o corpo `P_RESP` que segue um op code `op_response` já consumido.
77pub fn read_response_body(stream: &mut FbStream) -> Result<Response> {
78    let handle = stream.read_i32()?;
79    let blob_id = stream.read_quad()?;
80    let data = stream.read_bytes()?;
81    let status = read_status_vector(stream)?;
82    Ok(Response {
83        handle,
84        blob_id,
85        data,
86        status,
87    })
88}
89
90/// Lê o próximo pacote, exigindo que seja um `op_response`, e converte qualquer
91/// status de erro em [`Error::Database`].
92pub fn read_response(stream: &mut FbStream) -> Result<Response> {
93    let code = read_op(stream)?;
94    if code != op::RESPONSE {
95        // Recebemos um pacote que não esperávamos: o stream está fora de sincronia
96        // e não pode ser reutilizado com segurança.
97        stream.mark_broken();
98        return Err(Error::protocol(format!(
99            "expected op_response, got {} ({code})",
100            op_name(code)
101        )));
102    }
103    read_response_body(stream)?.into_result()
104}