use crate::core::arithmetic_engine::{ArithmeticEngine, BigDecimalEngine};
use crate::core::environment::model_to_string;
use crate::core::eval::eval;
use crate::core::Expr;
use crate::error::Result;
use crate::template::TModel;
use indexmap::IndexMap;
use crate::core::built_ins_for_markup_outputs::escape_plain_text;
pub struct AddConcatExpression {
pub left: Expr,
pub right: Expr,
}
impl AddConcatExpression {
pub fn new(left: Expr, right: Expr) -> Self {
AddConcatExpression { left, right }
}
pub(crate) fn eval(&self, env: &mut crate::core::Environment) -> Result<TModel> {
eval_add(env, &self.left, &self.right)
}
}
pub(crate) fn eval_add(env: &mut crate::core::Environment, a: &Expr, b: &Expr) -> Result<TModel> {
let l = eval(env, a)?;
let r = eval(env, b)?;
if l.is_number() && r.is_number() {
let engine = BigDecimalEngine::default();
return Ok(TModel::from_number(
engine.add(&l.get_number()?, &r.get_number()?)?,
));
}
if l.is_sequence() && r.is_sequence() {
let ls = l.get_sequence()?;
let rs = r.get_sequence()?;
let n = ls.size()? + rs.size()?;
let mut v = Vec::with_capacity(n);
for i in 0..ls.size()? {
v.push(ls.get(i)?);
}
for i in 0..rs.size()? {
v.push(rs.get(i)?);
}
return Ok(TModel::from_sequence(v));
}
if l.is_hash() && r.is_hash() {
let lh = l.get_hash()?;
let rh = r.get_hash()?;
let mut map = IndexMap::new();
if let Some(ex) = &l.hash_ex {
for key in ex.keys()? {
if let Some(v) = lh.get(&key)? {
map.entry(key).or_insert(v);
}
}
}
if let Some(ex) = &r.hash_ex {
for key in ex.keys()? {
if let Some(v) = rh.get(&key)? {
map.entry(key).or_insert(v);
}
}
}
for key in map.keys().cloned().collect::<Vec<_>>() {
if let Some(v) = rh.get(&key)? {
map.insert(key, v);
}
}
return Ok(TModel::from_hash(map));
}
let l_markup = l.is_markup_output();
let r_markup = r.is_markup_output();
if l_markup || r_markup {
let ls = model_to_string(env, &l)?;
let rs = model_to_string(env, &r)?;
let (fmt, out, plain) = match (l_markup, r_markup) {
(true, true) => {
let lf = l.markup_format.unwrap_or(env.settings.output_format);
let rf = r.markup_format.unwrap_or(env.settings.output_format);
if lf == rf {
(
lf,
format!("{ls}{rs}"),
concat_plain(&l.markup_plain, &r.markup_plain),
)
} else if let Some(rp) = &r.markup_plain {
(
lf,
format!("{ls}{}", escape_plain_text(lf, rp)),
concat_plain(&l.markup_plain, &r.markup_plain),
)
} else if let Some(lp) = &l.markup_plain {
(
rf,
format!("{}{rs}", escape_plain_text(rf, lp)),
concat_plain(&l.markup_plain, &r.markup_plain),
)
} else {
return Err(crate::error::TemplateError::misc(format!(
"Concatenation left hand operand is in {} format, while the right \
hand operand is in {}. Conversion to common format wasn't possible.",
lf.name(),
rf.name()
)));
}
}
(true, false) => {
let lf = l.markup_format.unwrap_or(env.settings.output_format);
(
lf,
format!("{ls}{}", escape_plain_text(lf, &rs)),
l.markup_plain.as_ref().map(|p| format!("{p}{rs}")),
)
}
(false, true) => {
let rf = r.markup_format.unwrap_or(env.settings.output_format);
(
rf,
format!("{}{rs}", escape_plain_text(rf, &ls)),
r.markup_plain.as_ref().map(|p| format!("{ls}{p}")),
)
}
(false, false) => unreachable!(),
};
return Ok(crate::core::built_ins_for_markup_outputs::markup_model_with(out, plain, fmt));
}
let ls = model_to_string(env, &l)?;
let rs = model_to_string(env, &r)?;
Ok(TModel::from_scalar(ls + &rs))
}
fn concat_plain(a: &Option<String>, b: &Option<String>) -> Option<String> {
match (a, b) {
(Some(x), Some(y)) => Some(format!("{x}{y}")),
_ => None,
}
}