use super::{AbstractDialect, AbstractDialectError, IDialect};
pub struct AbstractProcessorDialect {
dialect: AbstractDialect,
prefix: Option<String>,
processor_precedence: i32,
}
impl AbstractProcessorDialect {
pub fn new(
name: Option<&str>,
prefix: Option<&str>,
processor_precedence: i32,
) -> Result<Self, AbstractDialectError> {
let dialect = AbstractDialect::new(name)?;
Ok(Self {
dialect,
prefix: prefix.map(str::to_owned),
processor_precedence,
})
}
#[must_use]
pub fn get_name(&self) -> &str {
self.dialect.get_name()
}
#[must_use]
pub fn get_prefix(&self) -> Option<&str> {
self.prefix.as_deref()
}
#[must_use]
pub const fn get_dialect_processor_precedence(&self) -> i32 {
self.processor_precedence
}
}
impl IDialect for AbstractProcessorDialect {
fn class_name(&self) -> &'static str {
"org.thymeleaf.dialect.AbstractProcessorDialect"
}
fn get_name(&self) -> Option<&str> {
Some(AbstractProcessorDialect::get_name(self))
}
}
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::{AbstractDialectError, AbstractProcessorDialect};
use crate::dialect::{IDialect, IProcessorDialect};
use crate::processor::ProcessorSet;
struct ProbeDialect {
base: AbstractProcessorDialect,
calls: AtomicUsize,
last_prefix: Mutex<Option<Option<String>>>,
}
impl IDialect for ProbeDialect {
fn get_name(&self) -> Option<&str> {
Some(self.base.get_name())
}
}
impl IProcessorDialect for ProbeDialect {
fn get_prefix(&self) -> Option<&str> {
self.base.get_prefix()
}
fn get_dialect_processor_precedence(&self) -> i32 {
self.base.get_dialect_processor_precedence()
}
fn get_processors(&self, dialect_prefix: Option<&str>) -> Option<ProcessorSet> {
self.calls.fetch_add(1, Ordering::SeqCst);
*self.last_prefix.lock().expect("last prefix lock") =
Some(dialect_prefix.map(str::to_owned));
Some(ProcessorSet::new())
}
}
#[test]
fn rejects_null_name_through_parent_constructor_with_exact_error() {
let error = AbstractProcessorDialect::new(None, Some("ignored"), i32::MAX)
.err()
.expect("null name must fail");
assert_eq!(error, AbstractDialectError::DialectNameCannotBeNull);
assert_eq!(error.to_string(), "Dialect name cannot be null");
}
#[test]
fn preserves_composed_final_state_and_concrete_processor_extension_dispatch() {
for (name, prefix, precedence) in [
("", None, i32::MIN),
("empty-prefix", Some(""), 0),
("方言", Some("前缀"), i32::MAX),
] {
let base = AbstractProcessorDialect::new(Some(name), prefix, precedence)
.expect("non-null name is valid");
let implementation = ProbeDialect {
base,
calls: AtomicUsize::new(0),
last_prefix: Mutex::new(None),
};
let contract: &dyn IProcessorDialect = &implementation;
assert_eq!(implementation.base.get_name(), name);
assert_eq!(implementation.base.get_prefix(), prefix);
assert_eq!(
implementation.base.get_dialect_processor_precedence(),
precedence
);
assert_eq!(IDialect::get_name(&implementation.base), Some(name));
assert_eq!(contract.get_name(), Some(name));
assert_eq!(contract.get_prefix(), prefix);
assert_eq!(contract.get_dialect_processor_precedence(), precedence);
let actual_prefix = Some("用户覆盖");
assert!(
contract
.get_processors(actual_prefix)
.expect("probe returns an empty set")
.is_empty()
);
assert_eq!(implementation.calls.load(Ordering::SeqCst), 1);
assert_eq!(
*implementation.last_prefix.lock().expect("last prefix lock"),
Some(actual_prefix.map(str::to_owned))
);
assert_eq!(implementation.base.get_name(), name);
assert_eq!(implementation.base.get_prefix(), prefix);
assert_eq!(
implementation.base.get_dialect_processor_precedence(),
precedence
);
}
}
}