use mf2_catalog::Catalog;
use mf2_model::Dir;
use crate::datetime::TimeZone;
use crate::error::FormatError;
use crate::host::Host;
use crate::scratch::Scratch;
use crate::sink::{ErrorSink, Sink, SubPartSink};
use crate::unannotated;
use crate::value::Value;
pub trait Function: Sync {
fn resolve<'a>(
&self,
cx: &FnContext<'_>,
operand: Option<&Value<'a>>,
options: &Options<'_, 'a>,
errs: &mut dyn ErrorSink,
) -> Option<Value<'a>>;
fn formattable(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Result<(), FormatError> {
let _ = (cx, value);
Ok(())
}
fn format(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn Sink);
fn format_parts(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn SubPartSink) {
let _ = (cx, value, out);
}
fn part_kind(&self) -> &'static str {
"string"
}
fn dir(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Dir {
let _ = (cx, value);
Dir::Auto
}
fn selectable(&self, value: &Value<'_>) -> bool {
let _ = value;
false
}
fn matches(
&self,
cx: &FnContext<'_>,
value: &Value<'_>,
key: &str,
errs: &mut dyn ErrorSink,
) -> bool {
let _ = (cx, value, key, errs);
false
}
fn better_than(&self, cx: &FnContext<'_>, value: &Value<'_>, key1: &str, key2: &str) -> bool {
let _ = (cx, value, key1, key2);
false
}
}
#[derive(Clone, Copy)]
pub struct FnContext<'x> {
pub(crate) catalog: &'x Catalog,
pub(crate) host: &'static dyn Host,
pub(crate) dir: Option<Dir>,
pub(crate) time_zone: &'x TimeZone,
}
impl<'x> FnContext<'x> {
pub fn locale(&self) -> &'x str {
self.catalog.locale()
}
pub fn dir(&self) -> Option<Dir> {
self.dir
}
pub fn host(&self) -> &'static dyn Host {
self.host
}
#[doc(hidden)]
pub fn catalog(&self) -> &'x Catalog {
self.catalog
}
pub fn time_zone(&self) -> &'x TimeZone {
self.time_zone
}
}
impl core::fmt::Debug for FnContext<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("FnContext")
.field("catalog", self.catalog)
.field("dir", &self.dir)
.field("time_zone", self.time_zone)
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct OptionValue<'o, 'a> {
pub value: &'o Value<'a>,
pub literal: bool,
}
pub(crate) type OptionList<'o, 'a> = Scratch<(&'a str, OptionValue<'o, 'a>)>;
#[derive(Clone, Copy)]
pub(crate) struct OptionEntries<'o, 'a> {
list: Option<&'o OptionList<'o, 'a>>,
}
impl<'o, 'a> OptionEntries<'o, 'a> {
pub(crate) fn new(list: &'o OptionList<'o, 'a>) -> Self {
OptionEntries { list: Some(list) }
}
pub(crate) fn len(self) -> usize {
self.list.map_or(0, Scratch::len)
}
pub(crate) fn get(self, i: usize) -> Option<&'o (&'a str, OptionValue<'o, 'a>)> {
self.list?.get(i)
}
}
#[derive(Clone, Copy)]
pub struct Options<'o, 'a> {
pub(crate) entries: OptionEntries<'o, 'a>,
}
impl<'o, 'a> Options<'o, 'a> {
pub fn get(&self, name: &str) -> Option<OptionValue<'o, 'a>> {
let mut found = None;
for i in 0..self.entries.len() {
if let Some((n, v)) = self.entries.get(i)
&& *n == name
{
found = Some(*v);
}
}
found
}
pub fn iter(&self) -> impl Iterator<Item = (&'a str, OptionValue<'o, 'a>)> + 'o {
let entries = self.entries;
(0..entries.len()).filter_map(move |i| entries.get(i).copied())
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.len() == 0
}
}
impl core::fmt::Debug for Options<'_, '_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Options")
.field("len", &self.entries.len())
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy)]
pub struct Registry {
functions: &'static [(&'static str, &'static dyn Function)],
numbers: Option<&'static dyn Function>,
dates: Option<&'static dyn Function>,
}
impl Registry {
pub const EMPTY: Registry = Registry {
functions: &[],
numbers: None,
dates: None,
};
pub const fn new(functions: &'static [(&'static str, &'static dyn Function)]) -> Registry {
Registry {
functions,
numbers: None,
dates: None,
}
}
#[must_use]
pub const fn with_numbers(self, f: &'static dyn Function) -> Registry {
Registry {
numbers: Some(f),
..self
}
}
#[must_use]
pub const fn with_dates(self, f: &'static dyn Function) -> Registry {
Registry {
dates: Some(f),
..self
}
}
pub(crate) fn unannotated(&self, v: &Value<'_>) -> Option<&'static dyn Function> {
if unannotated::is_numeric(v) {
self.numbers
} else if unannotated::is_date_time(v) {
self.dates
} else {
None
}
}
pub fn get(&self, name: &str) -> Option<&'static dyn Function> {
self.functions
.iter()
.find(|(n, _)| *n == name)
.map(|&(_, f)| f)
}
}
impl core::fmt::Debug for Registry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list()
.entries(self.functions.iter().map(|(n, _)| n))
.finish()
}
}