xbf_rs 0.1.0

The reference implementation for the XBF format.
Documentation
use anyhow::{Context, Result};
use byteorder::WriteBytesExt;
use std::{
    io::{BufReader, BufWriter, Write},
    net::{TcpStream, ToSocketAddrs},
};
use xbf_rs::{XbfMetadata, XbfPrimitive, XbfStruct, XbfType};

fn find_galbatorix(value: &XbfType) -> Option<&XbfStruct> {
    if let XbfType::Vec(v) = value {
        for value in v {
            if let XbfType::Struct(s) = value {
                if let Some(XbfType::Primitive(XbfPrimitive::String(name))) = s.get("name") {
                    if name == "Galbatorix" {
                        return Some(s);
                    }
                }
            }
        }
    }
    None
}

fn get_connection(
    addr: impl ToSocketAddrs,
) -> Result<(BufWriter<TcpStream>, BufReader<TcpStream>)> {
    let stream = TcpStream::connect(addr).context("Unable to connect to server")?;
    let writer = BufWriter::new(stream.try_clone().context("Unable to clone stream")?);
    let reader = BufReader::new(stream);
    Ok((writer, reader))
}

fn get_data_and_metadata(addr: impl ToSocketAddrs) -> Result<(XbfMetadata, XbfType)> {
    let (mut writer, mut reader) = get_connection(addr)?;
    writer.write_u8(0)?;
    writer.flush()?;
    let metadata = XbfMetadata::deserialize_base_metadata(&mut reader)?;
    let value = XbfType::deserialize_base_type(&metadata, &mut reader)?;
    Ok((metadata, value))
}

fn get_data(addr: impl ToSocketAddrs, known_metadata: &XbfMetadata) -> Result<XbfType> {
    let (mut writer, mut reader) = get_connection(addr)?;
    writer.write_u8(1)?;
    writer.flush()?;
    let value = XbfType::deserialize_base_type(known_metadata, &mut reader)?;
    Ok(value)
}

fn unknown_request(addr: impl ToSocketAddrs) -> Result<(XbfMetadata, XbfType)> {
    let (mut writer, mut reader) = get_connection(addr)?;
    writer.write_u8(2)?;
    writer.flush()?;
    let metadata = XbfMetadata::deserialize_base_metadata(&mut reader)?;
    let value = XbfType::deserialize_base_type(&metadata, &mut reader)?;
    Ok((metadata, value))
}

fn main() -> Result<()> {
    let (metadata, value) = get_data_and_metadata("127.0.0.1:6969")?;
    println!("{:?}", find_galbatorix(&value));

    let value = get_data("127.0.0.1:6969", &metadata)?;
    println!("{:?}", find_galbatorix(&value));

    let unnown_request = unknown_request("127.0.0.1:6969")?;
    println!("{:?}", unnown_request);

    Ok(())
}