use std::str::FromStr;
use crate::logging::*;
use crate::util::errors::io_error_invalid_data;
pub fn parse_string_vec_naive(line: &str) -> Vec<Box<str>> {
if line.is_empty() {
return Vec::with_capacity(0);
}
let item_count = line.as_bytes().iter().filter(|&&b| b == b' ').count() + 1;
let mut res: Vec<Box<str>> = Vec::with_capacity(item_count);
for item in line.split(' ') {
res.push(Box::from(item));
}
res
}
pub struct Connected {
pub server_info: ServerInfo,
}
impl FromStr for Connected {
type Err = std::io::Error;
fn from_str(line: &str) -> Result<Self, Self::Err> {
let Some((discriminant, rest)) = line.split_once(' ') else {
return Err(io_error_invalid_data("Line missing discriminant"));
};
if discriminant != "CONNECTED" {
return Err(io_error_invalid_data(format!(
"Incorrect line discriminant: expected \"CONNECTED\", got {discriminant:?}"
)));
}
let server_info = ServerInfo::from_str(rest)
.inspect_err(|error| log_error!("{error}"))
.unwrap_or_default();
Ok(Self { server_info })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServerInfo {
pub version: Box<str>,
pub additional_data: Box<str>,
}
impl Default for ServerInfo {
fn default() -> Self {
Self {
version: Box::from("unknown"),
additional_data: Box::default(),
}
}
}
impl std::str::FromStr for ServerInfo {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let Some(rest) = s.strip_prefix('<') else {
return Err(format!("Missing '<' in {s:?}"));
};
let Some((version, rest)) = rest.split_once('>') else {
return Err(format!("Missing '<' in {s:?}"));
};
Ok(Self {
version: Box::from(version),
additional_data: Box::from(rest.trim_ascii_start()),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ChannelInfo {
pub protocol_version: u8,
pub buffer_size: usize,
}
impl std::str::FromStr for ChannelInfo {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut protocol_version: Option<u8> = None;
let mut buffer_size: Option<usize> = None;
for arg in s.split(' ') {
let Some(stripped) = arg.strip_suffix(')') else {
return Err(io_error_invalid_data(format!(
"Arg does not end with ')': {arg:?}"
)));
};
let Some((key, value)) = stripped.split_once('(') else {
return Err(io_error_invalid_data(format!(
"Arg does not contain '(': {arg:?}"
)));
};
macro_rules! update {
($store:ident with $value:ident) => {{
let new_value = $value.parse().map_err(io_error_invalid_data)?;
let old_value = $store.replace(new_value);
if let Some(old_value) = old_value {
eprintln!("{key:?} was provided multiple times, using new value (old: {old_value}, new: {new_value}).");
}
}};
}
match (key, value) {
("protocol", v) => update!(protocol_version with v),
("buffer", v) => update!(buffer_size with v),
_ => eprintln!("Unknown info: {arg:?}"),
}
}
macro_rules! info_not_found {
($key:literal) => {
io_error_invalid_data(format!("Key {key:?} not found in {s:?}", key = $key))
};
}
let Some(protocol_version) = protocol_version else {
return Err(info_not_found!("protocol"));
};
let Some(buffer_size) = buffer_size else {
return Err(info_not_found!("buffer"));
};
Ok(Self {
protocol_version,
buffer_size,
})
}
}