use crate::core::Environment;
use crate::error::{Result, TemplateError};
use crate::template::TModel;
fn arg_to_string(m: &TModel) -> Result<String> {
if let Some(s) = &m.scalar {
return s.as_string();
}
if let Some(n) = &m.number {
let num = n.as_number()?;
if let Some(v) = num.as_i64() {
return Ok(v.to_string());
}
return Ok(num.to_plain_string());
}
if let Some(b) = &m.boolean {
return Ok(b.as_boolean()?.to_string());
}
Ok(m.type_name.to_string())
}
pub fn new_utility_class(class_name: &str, ctor_args: &[TModel]) -> Result<TModel> {
match class_name {
"freemarker.template.utility.StandardCompress" => {
if !ctor_args.is_empty() {
return Err(no_such_method(class_name, ctor_args));
}
Ok(TModel::from_transform(crate::template::utility::StandardCompressTransform))
}
"freemarker.template.utility.NormalizeNewlines" => {
if !ctor_args.is_empty() {
return Err(no_such_method(class_name, ctor_args));
}
Ok(TModel::from_transform(crate::template::utility::NormalizeNewlinesTransform))
}
"freemarker.template.utility.HtmlEscape" => {
if !ctor_args.is_empty() {
return Err(no_such_method(class_name, ctor_args));
}
Ok(TModel::from_transform(crate::template::utility::HtmlEscapeTransform))
}
"freemarker.test.templatesuite.models.NewTestModel" => {
new_test_model(ctor_args)
}
"freemarker.test.templatesuite.models.NewTestModel2" => {
new_test_model(ctor_args)
}
"freemarker.template.utility.ObjectConstructor" => {
Ok(TModel::from_method(crate::template::utility::ObjectConstructorFn))
}
"freemarker.test.templatesuite.models.SimpleTestMethod" => {
Ok(TModel::from_method(SimpleTestMethodFn))
}
_ => Err(TemplateError::misc(format!(
"No error description was specified for this error; low-level message: java.lang.ClassNotFoundException: {class_name}"
))),
}
}
fn no_such_method(class_name: &str, ctor_args: &[TModel]) -> TemplateError {
let arg_desc: Vec<&str> = ctor_args.iter().map(|m| m.type_name).collect();
TemplateError::misc(format!(
"No error description was specified for this error; low-level message: java.lang.NoSuchMethodException: {class_name}.<init>({}).",
arg_desc.join(", ")
))
}
fn new_test_model(args: &[TModel]) -> Result<TModel> {
let text = match args {
[] => "default constructor".to_string(),
[a] if a.scalar.is_some() || a.is_number() => arg_to_string(a)?,
[a, b] => format!("{}:{}", arg_to_string(a)?, arg_to_string(b)?),
_ => {
return Err(no_such_method(
"freemarker.test.templatesuite.models.NewTestModel",
args,
))
}
};
Ok(TModel::from_scalar(text))
}
pub struct SimpleTestMethodFn;
impl crate::template::TemplateMethodModelEx for SimpleTestMethodFn {
fn exec(&self, _env: &mut Environment, args: Vec<TModel>) -> Result<TModel> {
let value = if let Some(arg) = args.first() {
arg_to_test_value(arg)
} else {
"".to_string()
};
Ok(TModel::from_scalar(format!(
"Single argument value is: {value}"
)))
}
}
fn arg_to_test_value(m: &TModel) -> String {
use std::collections::HashMap;
if let Some(n) = &m.number {
let num = n.as_number().ok();
if let Some(v) = num.clone().and_then(|n| n.as_i64()) {
return v.to_string();
}
if let Some(v) = num.map(|n| n.to_plain_string()) {
return v;
}
}
if let Some(s) = &m.scalar {
let sv = s.as_string().unwrap_or_default();
let word_map: HashMap<&str, &str> = [
("zero", "0"),
("one", "1"),
("two", "2"),
("three", "3"),
("four", "4"),
("five", "5"),
("six", "6"),
("seven", "7"),
("eight", "8"),
("nine", "9"),
("ten", "10"),
("eleven", "11"),
("twelve", "12"),
]
.iter()
.cloned()
.collect();
if let Some(num) = word_map.get(sv.as_str()) {
return num.to_string();
}
if let Ok(n) = sv.parse::<i64>() {
return n.to_string();
}
let digits: String = sv.chars().filter(|c| c.is_ascii_digit()).collect();
if !digits.is_empty() {
return digits;
}
return sv;
}
"".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compress_state_machine_matches_java() {
assert_eq!(
crate::template::utility::standard_compress_text(" a\n\n b\r\nc\r d ", false),
"a\nb\r\nc\rd"
);
assert_eq!(
crate::template::utility::standard_compress_text("x y", false),
"x y"
);
assert_eq!(
crate::template::utility::standard_compress_text("\n\nx", false),
"x"
);
assert_eq!(
crate::template::utility::standard_compress_text("a \t b", false),
"a b"
);
assert_eq!(
crate::template::utility::standard_compress_text("a\nb\n", true),
"a b"
);
assert_eq!(
crate::template::utility::standard_compress_text("\n a\nb\n\n", true),
"a b"
);
}
#[test]
fn normalize_newlines_lines() {
assert_eq!(
crate::template::utility::normalize_newlines_text("a\r\nb\rc\n"),
"a\nb\nc\n"
);
assert_eq!(
crate::template::utility::normalize_newlines_text("\r\nb"),
"b\n"
);
assert_eq!(crate::template::utility::normalize_newlines_text(""), "");
assert_eq!(
crate::template::utility::normalize_newlines_text("a\n\nb"),
"a\n\nb\n"
);
assert_eq!(
crate::template::utility::normalize_newlines_text("no-eol"),
"no-eol\n"
);
assert_eq!(crate::template::utility::normalize_newlines_text("\n"), "");
}
#[test]
fn html_escape_entity_set() {
assert_eq!(
crate::template::utility::html_escape_entity("<a href=\"x\">&'\n"),
"<a href="x">&'\n"
);
assert_eq!(
crate::template::utility::html_escape_entity("plain"),
"plain"
);
}
#[test]
fn new_test_model_constructors() {
assert_eq!(
new_utility_class("freemarker.test.templatesuite.models.NewTestModel", &[])
.unwrap()
.scalar
.unwrap()
.as_string()
.unwrap(),
"default constructor"
);
assert_eq!(
new_utility_class(
"freemarker.test.templatesuite.models.NewTestModel",
&[TModel::from_scalar("xxx".to_string())]
)
.unwrap()
.scalar
.unwrap()
.as_string()
.unwrap(),
"xxx"
);
assert_eq!(
new_utility_class(
"freemarker.test.templatesuite.models.NewTestModel",
&[TModel::from_number(crate::value::TNumber::Int(1))]
)
.unwrap()
.scalar
.unwrap()
.as_string()
.unwrap(),
"1"
);
assert_eq!(
new_utility_class(
"freemarker.test.templatesuite.models.NewTestModel",
&[
TModel::from_scalar("xxx".to_string()),
TModel::from_scalar("yyy".to_string())
]
)
.unwrap()
.scalar
.unwrap()
.as_string()
.unwrap(),
"xxx:yyy"
);
let err = new_utility_class("no.such.Class", &[]).unwrap_err();
assert!(err
.to_string()
.contains("ClassNotFoundException: no.such.Class"));
let err = new_utility_class(
"freemarker.template.utility.StandardCompress",
&[TModel::from_scalar("x".to_string())],
)
.unwrap_err();
assert!(err.to_string().contains("NoSuchMethodException"));
}
}