use alloc::{
string::{String, ToString},
vec::Vec,
};
use crate::{DemangleConfig, DemangleError};
use crate::dem_arg::{demangle_argument, DemangledArg};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum ProcessedArg {
Plain(String),
Lookback { index: usize },
Ellipsis,
}
#[derive(Debug)]
pub(crate) struct ArgVec<'c, 'ns> {
config: &'c DemangleConfig,
namespace: Option<&'ns str>,
args: Vec<ProcessedArg>,
trailing_ellipsis: bool,
}
impl<'c, 'ns> ArgVec<'c, 'ns> {
pub(crate) fn new(config: &'c DemangleConfig, namespace: Option<&'ns str>) -> Self {
Self {
config,
namespace,
args: Vec::new(),
trailing_ellipsis: false,
}
}
pub(crate) fn get(&self, mut index: usize) -> Option<&str> {
if let Some(namespace) = self.namespace {
if index == 0 {
return Some(namespace);
} else {
index -= 1;
}
}
loop {
let arg = self.args.get(index)?;
match arg {
ProcessedArg::Plain(p) => break Some(p),
ProcessedArg::Lookback { index: i } => {
if *i >= index {
break None;
}
index = *i;
}
ProcessedArg::Ellipsis => break Some("..."),
}
}
}
pub(crate) fn push<'s>(
&mut self,
arg: DemangledArg,
s: &'s str,
remaining: &'s str,
allow_data_after_ellipsis: bool,
) -> Result<bool, DemangleError<'s>> {
let mut found_end = false;
let arg = match arg {
DemangledArg::Plain(plain, array_qualifiers) => {
ProcessedArg::Plain(format!("{plain}{array_qualifiers}"))
}
DemangledArg::FunctionPointer(function_pointer) => {
ProcessedArg::Plain(function_pointer.to_string())
}
DemangledArg::MethodPointer(method_pointer) => {
ProcessedArg::Plain(method_pointer.to_string())
}
DemangledArg::Repeat { count, index } => {
if self.namespace.is_some() {
if index == 0 {
} else {
let i = index - 1;
if i >= self.args.len() {
return Err(DemangleError::InvalidRepeatingArgument(s));
}
}
} else if index >= self.args.len() {
return Err(DemangleError::InvalidRepeatingArgument(s));
}
for _ in 0..count.get() - 1 {
self.args.push(ProcessedArg::Lookback { index });
}
ProcessedArg::Lookback { index }
}
DemangledArg::Ellipsis => {
if !allow_data_after_ellipsis && !remaining.is_empty() {
return Err(DemangleError::TrailingDataAfterEllipsis(remaining));
}
found_end = true;
if !self.config.ellipsis_emit_space_after_comma {
self.trailing_ellipsis = true;
return Ok(found_end);
}
ProcessedArg::Ellipsis
}
};
self.args.push(arg);
Ok(found_end)
}
pub(crate) fn join(self) -> String {
let mut args = Vec::with_capacity(self.args.len());
for arg in &self.args {
match arg {
ProcessedArg::Plain(plain) => args.push(plain.as_str()),
ProcessedArg::Lookback { index } => {
let arg = if let Some(namespace) = self.namespace {
if *index == 0 {
namespace
} else {
args.get(*index - 1)
.expect("Indices were verified when pushing the arguments")
}
} else {
args.get(*index)
.expect("Indices were verified when pushing the arguments")
};
args.push(arg);
}
ProcessedArg::Ellipsis => args.push("..."),
}
}
let mut out = args.join(", ");
if self.trailing_ellipsis {
if !out.is_empty() {
out.push(',');
}
out.push_str("...");
}
out
}
}
pub(crate) fn demangle_argument_list<'s>(
config: &DemangleConfig,
args: &'s str,
namespace: Option<&str>,
template_args: &ArgVec,
allow_array_fixup: bool,
) -> Result<String, DemangleError<'s>> {
let (remaining, argument_list) = demangle_argument_list_impl(
config,
args,
namespace,
template_args,
false,
allow_array_fixup,
)?;
if !remaining.is_empty() {
return Err(DemangleError::TrailingDataAfterArgumentList(remaining));
}
Ok(argument_list.join())
}
pub(crate) fn demangle_argument_list_impl<'c, 's, 'ns>(
config: &'c DemangleConfig,
mut args: &'s str,
namespace: Option<&'ns str>,
template_args: &ArgVec,
allow_data_after_ellipsis: bool,
allow_array_fixup: bool,
) -> Result<(&'s str, ArgVec<'c, 'ns>), DemangleError<'s>> {
let mut arguments = ArgVec::new(config, namespace);
while !args.is_empty() && !args.starts_with('_') {
let old_args = args;
let (remaining, b) = demangle_argument(
config,
old_args,
&arguments,
template_args,
allow_array_fixup,
)?;
args = remaining;
if arguments.push(b, old_args, remaining, allow_data_after_ellipsis)? {
break;
}
}
Ok((args, arguments))
}