use crate::error::{DatabaseError, Error, Result, StatusArg, StatusVector};
use crate::wire::consts::{arg, op};
use crate::wire::stream::{FbStream, op_name};
#[derive(Debug, Clone)]
pub struct Response {
pub handle: i32,
pub blob_id: u64,
pub data: Vec<u8>,
pub status: StatusVector,
}
impl Response {
pub fn into_result(self) -> Result<Response> {
if self.status.is_error() {
return Err(Error::Database(DatabaseError::new(self.status)));
}
Ok(self)
}
}
pub fn read_op(stream: &mut FbStream) -> Result<i32> {
loop {
let code = stream.read_i32()?;
if code == op::DUMMY || code == op::VOID {
continue;
}
return Ok(code);
}
}
pub fn read_status_vector(stream: &mut FbStream) -> Result<StatusVector> {
let mut args = Vec::new();
let mut sql_state = None;
loop {
let tag = stream.read_i32()?;
match tag {
t if t == arg::END => break,
t if t == arg::GDS => args.push(StatusArg::Gds(stream.read_i32()?)),
t if t == arg::WARNING => args.push(StatusArg::Warning(stream.read_i32()?)),
t if t == arg::NUMBER => args.push(StatusArg::Number(stream.read_i32()?)),
t if t == arg::STRING || t == arg::CSTRING => {
let s = String::from_utf8_lossy(&stream.read_bytes()?).into_owned();
args.push(StatusArg::Str(s));
}
t if t == arg::INTERPRETED => {
let s = String::from_utf8_lossy(&stream.read_bytes()?).into_owned();
args.push(StatusArg::Interpreted(s));
}
t if t == arg::SQL_STATE => {
sql_state = Some(String::from_utf8_lossy(&stream.read_bytes()?).into_owned());
}
other => {
let _ = stream.read_i32()?;
args.push(StatusArg::Number(other));
}
}
}
Ok(StatusVector { args, sql_state })
}
pub fn read_response_body(stream: &mut FbStream) -> Result<Response> {
let handle = stream.read_i32()?;
let blob_id = stream.read_quad()?;
let data = stream.read_bytes()?;
let status = read_status_vector(stream)?;
Ok(Response {
handle,
blob_id,
data,
status,
})
}
pub fn read_response(stream: &mut FbStream) -> Result<Response> {
let code = read_op(stream)?;
if code != op::RESPONSE {
stream.mark_broken();
return Err(Error::protocol(format!(
"expected op_response, got {} ({code})",
op_name(code)
)));
}
read_response_body(stream)?.into_result()
}