use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use mf2_runtime::{Arg, ErrorSink, Formatter, MsgId, NoErrors, PartSink, Sink};
use crate::arg::{ArgValue, Text};
use crate::tr::with_args;
#[derive(Clone)]
pub struct TrDyn {
id: MsgId,
args: Box<[(Text, ArgValue)]>,
}
impl core::fmt::Debug for TrDyn {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("TrDyn { id: ")?;
crate::debug::msg_id(f, self.id)?;
f.write_str(", args: [")?;
for (i, (name, value)) in self.args.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
f.write_str("(")?;
crate::debug::quoted(f, name.as_str())?;
f.write_str(", ")?;
core::fmt::Debug::fmt(value, f)?;
f.write_str(")")?;
}
f.write_str("] }")
}
}
#[must_use]
pub fn tr_dyn(id: MsgId, args: impl Into<Box<[(Text, ArgValue)]>>) -> TrDyn {
TrDyn {
id,
args: args.into(),
}
}
impl TrDyn {
#[must_use]
pub fn new<N: Into<Text>, V: Into<ArgValue>>(
id: MsgId,
args: impl IntoIterator<Item = (N, V)>,
) -> TrDyn {
tr_dyn(
id,
args.into_iter()
.map(|(name, value)| (name.into(), value.into()))
.collect::<Vec<_>>(),
)
}
#[must_use]
pub const fn id(&self) -> MsgId {
self.id
}
#[must_use]
pub fn args(&self) -> &[(Text, ArgValue)] {
&self.args
}
pub fn write(&self, f: &Formatter<'_>, out: &mut dyn Sink, errs: &mut dyn ErrorSink) {
self.with_named(|args| f.write_named(self.id, args, out, errs));
}
pub fn parts(&self, f: &Formatter<'_>, out: &mut dyn PartSink, errs: &mut dyn ErrorSink) {
self.with_named(|args| f.parts_named(self.id, args, out, errs));
}
#[must_use]
pub fn format(&self, f: &Formatter<'_>) -> String {
let mut out = String::new();
self.write(f, &mut out, &mut NoErrors);
out
}
fn with_named<R>(&self, body: impl FnOnce(&[(&str, Arg<'_>)]) -> R) -> R {
let values: Vec<ArgValue> = self.args.iter().map(|(_, v)| v.clone()).collect();
with_args(&values, |lowered| {
let named: Vec<(&str, Arg<'_>)> = self
.args
.iter()
.map(|(name, _)| name.as_str())
.zip(lowered.iter().copied())
.collect();
body(&named)
})
}
}