#![allow(dead_code)]
#![allow(incomplete_features)]
use std::ffi::CStr;
use std::str;
pub use std::str::FromStr;
use nom::bytes::complete::take;
use nom::error::{ErrorKind, ParseError};
use base64ct::{Base64, Encoding};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Clone, Copy, PartialEq)]
pub struct FixedSizeString<const CAP: usize>
where
[(); CAP + 1]:,
{
data: [u8; CAP + 1],
len: usize,
}
impl<const CAP: usize> FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
pub fn empty() -> Self {
Self {
data: [0; CAP + 1],
len: 0,
}
}
pub fn from_bytes(input: &[u8]) -> NWNParseResult<Self> {
let (remaining_input, data_slice) = take(CAP as u8)(input)?;
let len = data_slice.iter().position(|&b| b == 0).unwrap_or(CAP);
let _str = str::from_utf8(&data_slice[..len]).map_err(|e| nom::Err::Error(e.into()))?;
let mut data = [0u8; CAP + 1];
data[..len].copy_from_slice(&data_slice[..len]);
Ok((remaining_input, Self { data, len }))
}
pub fn from_bytes_lossy(input: &[u8]) -> NWNParseResult<Self> {
let (remaining_input, data_slice) = take(CAP as u8)(input)?;
let len = data_slice.iter().position(|&b| b == 0).unwrap_or(CAP);
let s = String::from_utf8_lossy(&data_slice[..len]);
let len = std::cmp::min(s.len(), CAP);
let mut data = [0u8; CAP + 1];
data[..len].copy_from_slice(&s.as_bytes()[..len]);
Ok((remaining_input, Self { data, len }))
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(input: &str) -> Result<Self, &'static str> {
let input_bytes = input.as_bytes();
let len = input_bytes.len();
if len > CAP {
return Err("input string is too long");
}
let mut data = [0u8; CAP + 1];
data[..len].copy_from_slice(input_bytes);
Ok(Self { data, len })
}
pub fn as_bytes(&self) -> &[u8] {
&self.data[..CAP]
}
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.data[..self.len]) }
}
pub fn as_c_str(&self) -> &CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(&self.data[..self.len + 1]) }
}
pub fn set_str(&mut self, input: &str) -> Result<(), &'static str> {
let input_bytes = input.as_bytes();
if input_bytes.len() > CAP {
return Err("input string is too long");
}
self.len = input_bytes.len();
self.data[..self.len].copy_from_slice(input_bytes);
self.data[self.len..].fill(0);
Ok(())
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<const CAP: usize> std::str::FromStr for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
type Err = &'static str;
fn from_str(input: &str) -> Result<Self, Self::Err> {
FixedSizeString::from_str(input)
}
}
impl<const CAP: usize> std::fmt::Debug for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.as_str())
}
}
impl<const CAP: usize> std::fmt::Display for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(self.as_str())
}
}
impl<const CAP: usize> Default for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn default() -> Self {
Self {
data: [0; CAP + 1],
len: 0,
}
}
}
impl<const CAP: usize> Serialize for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de, const CAP: usize> Deserialize<'de> for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FixedSizeStringVisitor<const CAP: usize>
where
[(); CAP + 1]:, {}
impl<'de, const CAP: usize> serde::de::Visitor<'de> for FixedSizeStringVisitor<CAP>
where
[(); CAP + 1]:,
{
type Value = FixedSizeString<CAP>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a string containing at most {} characters", CAP)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Self::Value::from_str(s).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
})
}
}
deserializer.deserialize_str(FixedSizeStringVisitor {})
}
}
impl<const CAP: usize> PartialEq<&str> for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl<const CAP: usize> PartialEq<String> for FixedSizeString<CAP>
where
[(); CAP + 1]:,
{
fn eq(&self, other: &String) -> bool {
self.as_str() == other
}
}
pub fn parse_detect_u32(s: &str) -> Result<u32, std::num::ParseIntError> {
match s.get(..2) {
Some("0b") => u32::from_str_radix(&s[2..], 2),
Some("0o") => u32::from_str_radix(&s[2..], 8),
Some("0x") => u32::from_str_radix(&s[2..], 16),
_ => s.parse::<u32>(),
}
}
pub fn parse_detect_u16(s: &str) -> Result<u16, std::num::ParseIntError> {
match s.get(..2) {
Some("0b") => u16::from_str_radix(&s[2..], 2),
Some("0o") => u16::from_str_radix(&s[2..], 8),
Some("0x") => u16::from_str_radix(&s[2..], 16),
_ => s.parse::<u16>(),
}
}
pub fn parse_detect_u8(s: &str) -> Result<u8, std::num::ParseIntError> {
match s.get(..2) {
Some("0b") => u8::from_str_radix(&s[2..], 2),
Some("0o") => u8::from_str_radix(&s[2..], 8),
Some("0x") => u8::from_str_radix(&s[2..], 16),
_ => s.parse::<u8>(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_size_string() {
use std::ffi::CString;
let (_, s) = FixedSizeString::<4>::from_bytes(b"Yolo").unwrap();
assert_eq!(s, "Yolo");
assert_eq!(s.as_str(), "Yolo");
assert_eq!(s, "Yolo".to_string());
assert_eq!(s.as_c_str(), CString::new("Yolo").unwrap().as_c_str());
let (_, s) = FixedSizeString::<4>::from_bytes(b"Yol\0").unwrap();
assert_eq!(s, "Yol");
assert_eq!(s.as_str(), "Yol");
assert_eq!(s, "Yol".to_string());
assert_eq!(s.as_c_str(), CString::new("Yol").unwrap().as_c_str());
let (_, s) = FixedSizeString::<4>::from_bytes(b"Y\0l0").unwrap();
assert_eq!(s, "Y");
assert_eq!(s.as_c_str(), CString::new("Y").unwrap().as_c_str());
let (_, s) = FixedSizeString::<4>::from_bytes(&[0; 4]).unwrap();
assert_eq!(s, "");
assert_eq!(s.as_c_str(), CString::new("").unwrap().as_c_str());
assert!(FixedSizeString::<4>::from_bytes(&[0; 2]).is_err());
assert!(FixedSizeString::<4>::from_bytes(&[0xf0, 0x28, 0x8c, 0xbc]).is_err());
assert_eq!(FixedSizeString::<4>::from_str("Yolo").unwrap(), "Yolo");
assert!(FixedSizeString::<4>::from_str("Yolodqsd").is_err());
let mut s = FixedSizeString::<4>::from_str("ab").unwrap();
assert_eq!(s, "ab");
assert_eq!(s.len(), 2);
s.set_str("cdef").unwrap();
assert_eq!(s, "cdef");
assert_eq!(s.len(), 4);
assert!(s.set_str("cdefgh").is_err());
}
#[test]
fn test_parse_detect() {
assert_eq!(parse_detect_u8("0b0110").unwrap(), 0b0110);
assert_eq!(parse_detect_u8("0o027").unwrap(), 0o027);
assert_eq!(parse_detect_u8("0xA7").unwrap(), 0xA7);
assert_eq!(parse_detect_u8("34").unwrap(), 34);
assert!(parse_detect_u8("1324").is_err());
assert_eq!(parse_detect_u16("0b0110").unwrap(), 0b0110);
assert_eq!(parse_detect_u16("0o027").unwrap(), 0o027);
assert_eq!(parse_detect_u16("0xA7").unwrap(), 0xA7);
assert_eq!(parse_detect_u16("34").unwrap(), 34);
assert!(parse_detect_u16("132z4").is_err());
assert_eq!(parse_detect_u32("0b0110").unwrap(), 0b0110);
assert_eq!(parse_detect_u32("0o027").unwrap(), 0o027);
assert_eq!(parse_detect_u32("0xA7").unwrap(), 0xA7);
assert_eq!(parse_detect_u32("34").unwrap(), 34);
assert!(parse_detect_u32("132z4").is_err());
}
}
pub fn nom_parse_context<'a, O, E>(
context: &'static str,
res: nom::IResult<&'a [u8], O, E>,
) -> nom::IResult<&'a [u8], O, E>
where
E: nom::error::ContextError<&'a [u8]>,
{
res.map_err(|e| e.map(|error| E::add_context(&[][..], context, error)))
}
pub fn nom_context_error<'a, I: ?Sized>(
context: &'static str,
error_input: &'a I,
) -> nom::Err<nom::error::VerboseError<&'a I>> {
nom::Err::Error(nom::error::VerboseError {
errors: vec![(error_input, nom::error::VerboseErrorKind::Context(context))],
})
}
pub type NWNParseResult<'a, T> = Result<(&'a [u8], T), nom::Err<NWNParseError>>;
#[derive(Debug)]
pub struct NWNParseError {
errors: Vec<NWNParseErrorEnum>,
}
#[derive(Debug)]
pub enum NWNParseErrorEnum {
NomError(nom::error::Error<Vec<u8>>),
NomVerboseError((Vec<u8>, nom::error::VerboseErrorKind)),
Utf8Error(std::str::Utf8Error),
String(String),
}
impl NWNParseError {
pub fn new(err: NWNParseErrorEnum) -> Self {
Self { errors: vec![err] }
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(message: &str) -> Self {
Self {
errors: vec![NWNParseErrorEnum::String(message.to_owned())],
}
}
pub fn from_string(message: String) -> Self {
Self {
errors: vec![NWNParseErrorEnum::String(message)],
}
}
pub fn add_context_msg(mut self, message: String) -> Self {
self.errors.push(NWNParseErrorEnum::String(message));
self
}
pub fn push(&mut self, err: NWNParseErrorEnum) {
self.errors.push(err)
}
pub fn into_nom_err(self) -> nom::Err<Self> {
nom::Err::Error(self)
}
}
impl<ExternalError: std::fmt::Display> nom::error::FromExternalError<Vec<u8>, ExternalError>
for NWNParseError
{
fn from_external_error(_input: Vec<u8>, kind: ErrorKind, e: ExternalError) -> Self {
Self::from_string(format!("{}: ({:?}", e, kind))
}
}
impl ParseError<&[u8]> for NWNParseError {
fn from_error_kind(input: &[u8], kind: nom::error::ErrorKind) -> Self {
NWNParseError {
errors: vec![NWNParseErrorEnum::NomError(
nom::error::Error::from_error_kind(input.to_owned(), kind),
)],
}
}
fn append(input: &[u8], kind: nom::error::ErrorKind, mut other: Self) -> Self {
other.errors.push(NWNParseErrorEnum::NomError(
nom::error::Error::from_error_kind(input.to_owned(), kind),
));
other
}
fn from_char(input: &[u8], c: char) -> Self {
NWNParseError {
errors: vec![NWNParseErrorEnum::NomVerboseError((
input.to_owned(),
nom::error::VerboseErrorKind::Char(c),
))],
}
}
}
impl nom::error::ContextError<&[u8]> for NWNParseError {
fn add_context(input: &[u8], ctx: &'static str, mut other: Self) -> Self {
other.errors.push(NWNParseErrorEnum::NomVerboseError((
input.to_owned(),
nom::error::VerboseErrorKind::Context(ctx),
)));
other
}
}
impl From<nom::Err<nom::error::VerboseError<Vec<u8>>>> for NWNParseError {
fn from(nom_err: nom::Err<nom::error::VerboseError<Vec<u8>>>) -> Self {
let e = match nom_err {
nom::Err::Incomplete(_needed) => panic!("incomplete data"),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
};
NWNParseError {
errors: e
.errors
.into_iter()
.map(&NWNParseErrorEnum::NomVerboseError)
.collect(),
}
}
}
impl From<String> for NWNParseError {
fn from(err: String) -> Self {
NWNParseError {
errors: vec![NWNParseErrorEnum::String(err)],
}
}
}
impl From<&str> for NWNParseError {
fn from(err: &str) -> Self {
NWNParseError {
errors: vec![NWNParseErrorEnum::String(err.to_string())],
}
}
}
impl From<std::str::Utf8Error> for NWNParseError {
fn from(err: std::str::Utf8Error) -> Self {
NWNParseError {
errors: vec![NWNParseErrorEnum::Utf8Error(err)],
}
}
}
impl std::fmt::Display for NWNParseError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
for e in self.errors.iter() {
write!(fmt, "Parsing error:")?;
match e {
NWNParseErrorEnum::NomError(e) => {
write!(fmt, "\n- Failed to read data: {:?}", e.code)?
}
NWNParseErrorEnum::NomVerboseError((_input, kind)) => {
write!(fmt, "\n- Failed to read data: {:?}", kind)?
}
NWNParseErrorEnum::Utf8Error(e) => write!(fmt, "\n- {}", e)?,
NWNParseErrorEnum::String(msg) => write!(fmt, "\n- {}", msg)?,
}
}
Ok(())
}
}
pub fn serialize_base64<S>(data: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&Base64::encode_string(data))
}
pub fn deserialize_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = String;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a string")
}
fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(s.to_owned())
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(s.to_owned())
}
}
let data = deserializer.deserialize_string(Visitor)?;
use serde::de::Error;
Base64::decode_vec(&data).map_err(D::Error::custom)
}
pub fn dbg_dmp_str<'a, F, O, E: std::fmt::Debug>(
f: F,
context: &'static str,
) -> impl Fn(&'a str) -> nom::IResult<&'a str, O, E>
where
F: Fn(&'a str) -> nom::IResult<&'a str, O, E>,
O: std::fmt::Debug,
{
move |input: &'a str| match f(input) {
Err(e) => {
println!(
"{}: Error({:?}) at: {:?}",
context,
e,
&input[..std::cmp::min(input.len(), 30)]
);
Err(e)
}
Ok((input, res)) => {
eprintln!(
"{}: res={:?}, input={:?}",
context,
res,
&input[..std::cmp::min(input.len(), 30)]
);
Ok((input, res))
}
}
}
pub fn get_nom_input_offset(current_input: &[u8], complete_input: &[u8]) -> usize {
let current_ptr = current_input.as_ptr() as usize;
let complete_ptr = complete_input.as_ptr() as usize;
assert!(
current_ptr >= complete_ptr,
"current input is located before complete input"
);
let offset = current_ptr - complete_ptr;
assert!(
offset <= complete_input.len(),
"current input is located after the end of complete input"
);
offset
}
pub fn get_nom_input_linecol(current_input: &str, complete_input: &str) -> (usize, usize) {
let current_ptr = current_input.as_ptr() as usize;
let complete_ptr = complete_input.as_ptr() as usize;
assert!(
current_ptr >= complete_ptr,
"current input is located before complete input"
);
let offset = current_ptr - complete_ptr;
assert!(
offset <= complete_input.len(),
"current input is located after the end of complete input"
);
let mut line = 0;
let mut column = 0;
let before_str = unsafe { std::str::from_utf8_unchecked(&complete_input.as_bytes()[..offset]) };
for c in before_str.chars() {
match c {
'\n' => {
line += 1;
column = 0;
}
_ => {
column += 1;
}
}
}
(line, column)
}