use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use core::any::Any;
use mf2_catalog::markup_key;
use mf2_runtime::{Arg, DateTime, ErrorSink, Formatter, MsgId, NoErrors, PartSink, Sink};
use crate::arg::{ArgList, ArgValue, INLINE, to_arg};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Tr {
id: MsgId,
}
impl core::fmt::Debug for Tr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("Tr { id: ")?;
crate::debug::msg_id(f, self.id)?;
f.write_str(" }")
}
}
#[derive(Clone)]
pub struct TrArgs {
id: MsgId,
args: ArgList,
}
impl core::fmt::Debug for TrArgs {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("TrArgs { id: ")?;
crate::debug::msg_id(f, self.id)?;
f.write_str(", args: ")?;
core::fmt::Debug::fmt(&self.args, f)?;
f.write_str(" }")
}
}
#[derive(Clone)]
pub struct TrRich {
args: TrArgs,
handlers: Box<[(u64, Arc<dyn MarkupHandler>)]>,
}
impl core::fmt::Debug for TrRich {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("TrRich { id: ")?;
crate::debug::msg_id(f, self.args.id)?;
f.write_str(", args: ")?;
core::fmt::Debug::fmt(&self.args.args, f)?;
f.write_str(", handlers: ")?;
crate::debug::uint(f, self.handlers.len() as u64)?;
f.write_str(" }")
}
}
pub trait MarkupHandler: Send + Sync {
fn as_any(&self) -> &dyn Any;
}
#[must_use]
#[inline(always)]
#[allow(clippy::inline_always)]
pub const fn tr(id: MsgId) -> Tr {
Tr { id }
}
#[must_use]
pub const fn tr_args0(id: MsgId) -> TrArgs {
TrArgs {
id,
args: ArgList::EMPTY,
}
}
#[must_use]
pub fn tr_args1(id: MsgId, a: ArgValue) -> TrArgs {
TrArgs {
id,
args: ArgList::inline(1, [a, ArgValue::Unset, ArgValue::Unset, ArgValue::Unset]),
}
}
#[must_use]
pub fn tr_args2(id: MsgId, a: ArgValue, b: ArgValue) -> TrArgs {
TrArgs {
id,
args: ArgList::inline(2, [a, b, ArgValue::Unset, ArgValue::Unset]),
}
}
#[must_use]
pub fn tr_args3(id: MsgId, a: ArgValue, b: ArgValue, c: ArgValue) -> TrArgs {
TrArgs {
id,
args: ArgList::inline(3, [a, b, c, ArgValue::Unset]),
}
}
#[must_use]
pub fn tr_args4(id: MsgId, a: ArgValue, b: ArgValue, c: ArgValue, d: ArgValue) -> TrArgs {
TrArgs {
id,
args: ArgList::inline(4, [a, b, c, d]),
}
}
#[must_use]
pub fn tr_args_n(id: MsgId, args: Box<[ArgValue]>) -> TrArgs {
TrArgs {
id,
args: ArgList::spill(args),
}
}
#[must_use]
pub fn tr_rich(args: TrArgs, handlers: Box<[(u64, Arc<dyn MarkupHandler>)]>) -> TrRich {
TrRich { args, handlers }
}
impl Tr {
#[must_use]
pub const fn id(self) -> MsgId {
self.id
}
pub fn write(self, f: &Formatter<'_>, out: &mut dyn Sink, errs: &mut dyn ErrorSink) {
f.write(self.id, &[], out, errs);
}
pub fn parts(self, f: &Formatter<'_>, out: &mut dyn PartSink, errs: &mut dyn ErrorSink) {
f.parts(self.id, &[], out, errs);
}
#[must_use]
pub fn format(self, f: &Formatter<'_>) -> String {
let mut out = String::new();
self.write(f, &mut out, &mut NoErrors);
out
}
}
impl From<Tr> for TrArgs {
fn from(t: Tr) -> TrArgs {
tr_args0(t.id)
}
}
impl TrArgs {
#[must_use]
pub const fn id(&self) -> MsgId {
self.id
}
#[must_use]
pub fn args(&self) -> &[ArgValue] {
self.args.as_slice()
}
pub fn write(&self, f: &Formatter<'_>, out: &mut dyn Sink, errs: &mut dyn ErrorSink) {
with_args(self.args.as_slice(), |args| {
f.write(self.id, args, out, errs);
});
}
pub fn parts(&self, f: &Formatter<'_>, out: &mut dyn PartSink, errs: &mut dyn ErrorSink) {
with_args(self.args.as_slice(), |args| {
f.parts(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
}
}
impl TrRich {
#[must_use]
pub const fn id(&self) -> MsgId {
self.args.id
}
#[must_use]
pub const fn args(&self) -> &TrArgs {
&self.args
}
#[must_use]
pub fn handler(&self, name: &str) -> Option<&dyn MarkupHandler> {
let key = markup_key(name);
self.handlers
.iter()
.find(|(k, _)| *k == key)
.map(|(_, h)| &**h)
}
#[must_use]
pub fn handlers(&self) -> &[(u64, Arc<dyn MarkupHandler>)] {
&self.handlers
}
pub fn write(&self, f: &Formatter<'_>, out: &mut dyn Sink, errs: &mut dyn ErrorSink) {
self.args.write(f, out, errs);
}
pub fn parts(&self, f: &Formatter<'_>, out: &mut dyn PartSink, errs: &mut dyn ErrorSink) {
self.args.parts(f, out, errs);
}
#[must_use]
pub fn format(&self, f: &Formatter<'_>) -> String {
self.args.format(f)
}
}
pub(crate) fn with_args<R>(values: &[ArgValue], body: impl FnOnce(&[Arg<'_>]) -> R) -> R {
if values.iter().any(|v| matches!(v, ArgValue::Source(_))) {
let mut resolved = alloc::vec::Vec::new();
if resolved.try_reserve_exact(values.len()).is_err() {
return body(&[]);
}
resolved.extend(values.iter().map(ArgValue::resolve));
return with_args(&resolved, body);
}
with_dates(values, |dates| {
let mut inline = [Arg::Unset; INLINE];
let mut spill = alloc::vec::Vec::new();
let args: &[Arg<'_>] = if values.len() <= INLINE {
for (i, value) in values.iter().enumerate() {
if let Some(slot) = inline.get_mut(i) {
*slot = to_arg(value, dates.get(i).and_then(Option::as_ref));
}
}
inline.get(..values.len()).unwrap_or(&[])
} else {
if spill.try_reserve_exact(values.len()).is_err() {
return body(&[]);
}
spill.extend(
values
.iter()
.enumerate()
.map(|(i, value)| to_arg(value, dates.get(i).and_then(Option::as_ref))),
);
&spill
};
body(args)
})
}
fn with_dates<'a, R>(values: &'a [ArgValue], body: impl FnOnce(&[Option<DateTime<'a>>]) -> R) -> R {
if !values.iter().any(|v| matches!(v, ArgValue::DateTime(_))) {
return body(&[]);
}
let borrow = |value: &'a ArgValue| match value {
ArgValue::DateTime(d) => Some(d.borrow()),
_ => None,
};
if values.len() <= INLINE {
let mut inline: [Option<DateTime<'a>>; INLINE] = [None; INLINE];
for (i, value) in values.iter().enumerate() {
if let Some(slot) = inline.get_mut(i) {
*slot = borrow(value);
}
}
return body(inline.get(..values.len()).unwrap_or(&[]));
}
let mut spill = alloc::vec::Vec::new();
if spill.try_reserve_exact(values.len()).is_err() {
return body(&[]);
}
spill.extend(values.iter().map(borrow));
body(&spill)
}