use std::fmt;
use std::marker::PhantomData;
use bytes::{Buf, BufMut};
use crate::wire_format::WireFormat;
#[derive(Debug, Clone)]
pub struct SbpString<T, E = Unterminated> {
data: T,
encoding: PhantomData<E>,
}
impl<T, E> SbpString<T, E>
where
T: AsRef<[u8]>,
{
pub fn new(data: T) -> Self {
Self {
data,
encoding: PhantomData,
}
}
pub fn as_bytes(&self) -> &[u8] {
self.data.as_ref()
}
pub fn to_vec(&self) -> Vec<u8> {
self.data.as_ref().to_vec()
}
}
impl<E> From<String> for SbpString<Vec<u8>, E> {
fn from(s: String) -> Self {
SbpString::new(s.into_bytes())
}
}
impl<E> From<Vec<u8>> for SbpString<Vec<u8>, E> {
fn from(v: Vec<u8>) -> Self {
SbpString::new(v)
}
}
impl<T, E> fmt::Display for SbpString<T, E>
where
T: AsRef<[u8]>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(self.as_bytes()))
}
}
#[cfg(feature = "serde")]
impl<T, E> serde::Serialize for SbpString<T, E>
where
T: AsRef<[u8]>,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = String::from_utf8_lossy(self.as_bytes());
serializer.serialize_str(&s)
}
}
impl<E, const LEN: usize> WireFormat for SbpString<[u8; LEN], E> {
const MIN_LEN: usize = LEN;
fn len(&self) -> usize {
LEN
}
fn write<B: BufMut>(&self, buf: &mut B) {
self.data.write(buf)
}
fn parse_unchecked<B: Buf>(buf: &mut B) -> Self {
let data: [u8; LEN] = WireFormat::parse_unchecked(buf);
SbpString::new(data)
}
}
macro_rules! forward_payload_vec {
($encoding:ty, $min_len:expr) => {
impl WireFormat for SbpString<Vec<u8>, $encoding> {
const MIN_LEN: usize = $min_len;
fn len(&self) -> usize {
self.data.len()
}
fn write<B: BufMut>(&self, buf: &mut B) {
self.data.write(buf)
}
fn parse_unchecked<B: Buf>(buf: &mut B) -> Self {
let data: Vec<u8> = WireFormat::parse_unchecked(buf);
SbpString::new(data)
}
}
};
}
#[derive(Debug, Clone, Copy)]
pub struct Unterminated;
forward_payload_vec!(Unterminated, 0);
#[derive(Debug, Clone, Copy)]
pub struct NullTerminated;
forward_payload_vec!(NullTerminated, 1);
#[derive(Debug, Clone, Copy)]
pub struct Multipart;
forward_payload_vec!(Multipart, 0);
#[derive(Debug, Clone, Copy)]
pub struct DoubleNullTerminated;
forward_payload_vec!(DoubleNullTerminated, 2);
#[cfg(test)]
mod tests {
use bytes::BytesMut;
use super::*;
#[test]
fn test_parse_sbp_string_vec() {
let mut buf = BytesMut::from(&b"hi, imma string"[..]);
let actual: SbpString<Vec<u8>> = WireFormat::parse_unchecked(&mut buf);
assert_eq!(actual.to_string(), "hi, imma string".to_string());
assert!(buf.is_empty());
let actual: SbpString<Vec<u8>> = WireFormat::parse_unchecked(&mut buf);
assert_eq!(actual.to_string(), "".to_string());
}
#[test]
fn test_parse_sbp_string_array() {
let mut buf = BytesMut::from(&b"hi, imma string"[..]);
let actual: SbpString<[u8; 8]> = WireFormat::parse_unchecked(&mut buf);
assert_eq!(actual.to_string(), "hi, imma".to_string());
assert_eq!(buf.len(), 7);
let actual: SbpString<[u8; 7]> = WireFormat::parse_unchecked(&mut buf);
assert_eq!(actual.to_string(), " string".to_string());
assert!(buf.is_empty());
}
#[test]
fn test_parse_sbp_string_invalid_utf8() {
let mut buf = BytesMut::from(
&[
0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb6,
][..],
);
let sbp_string: SbpString<Vec<u8>> = WireFormat::parse_unchecked(&mut buf);
assert_eq!(sbp_string.to_string().len(), 69 + 3);
assert_eq!(sbp_string.to_vec().len(), 70);
}
}