use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use crate::util::Utf16String;
const RAW_PARSE_EXCEPTION_CLASS: &str = "org.thymeleaf.templateparser.raw.RawParseException";
pub struct RawParseCause {
error: Box<dyn Error + Send + Sync>,
class_name: String,
message: Option<Utf16String>,
raw_parse_location: Option<(i32, i32, Utf16String)>,
}
impl RawParseCause {
#[must_use]
pub fn with_java_metadata(
error: Box<dyn Error + Send + Sync>,
class_name: impl Into<String>,
message: Option<Utf16String>,
) -> Self {
Self {
error,
class_name: class_name.into(),
message,
raw_parse_location: None,
}
}
#[must_use]
pub fn from_raw_parse(exception: RawParseException) -> Self {
let message = exception.message.clone();
let raw_parse_location = exception.line.zip(exception.col).map(|(line, col)| {
(
line,
col,
message
.clone()
.expect("located RawParseException always has a message"),
)
});
Self {
error: Box::new(exception),
class_name: RAW_PARSE_EXCEPTION_CLASS.to_owned(),
message,
raw_parse_location,
}
}
#[must_use]
pub fn class_name(&self) -> &str {
&self.class_name
}
fn source_error(&self) -> &(dyn Error + 'static) {
self.error.as_ref()
}
}
impl Debug for RawParseCause {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("RawParseCause")
.field("class_name", &self.class_name)
.field("message", &self.message)
.field("raw_parse_location", &self.raw_parse_location)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct RawParseException {
message: Option<Utf16String>,
line: Option<i32>,
col: Option<i32>,
cause: Option<RawParseCause>,
}
impl RawParseException {
#[must_use]
pub const fn new() -> Self {
Self {
message: None,
line: None,
col: None,
cause: None,
}
}
#[must_use]
pub fn with_message(message: Option<Utf16String>) -> Self {
Self {
message,
line: None,
col: None,
cause: None,
}
}
#[must_use]
pub fn with_message_and_cause(
message: Option<Utf16String>,
cause: Option<RawParseCause>,
) -> Self {
let (line, col) = inherited_location(cause.as_ref());
let message = compose_inherited_message(message.as_ref(), cause.as_ref());
Self {
message,
line,
col,
cause,
}
}
#[must_use]
pub fn with_cause(cause: Option<RawParseCause>) -> Self {
Self::with_message_and_cause(None, cause)
}
#[must_use]
pub fn with_location(line: i32, col: i32) -> Self {
Self {
message: Some(message_prefix(line, col)),
line: Some(line),
col: Some(col),
cause: None,
}
}
#[must_use]
pub fn with_message_and_cause_at(
message: Option<&Utf16String>,
cause: Option<RawParseCause>,
line: i32,
col: i32,
) -> Self {
Self {
message: Some(prefix_with_message(line, col, message)),
line: Some(line),
col: Some(col),
cause,
}
}
#[must_use]
pub fn with_message_at(message: Option<&Utf16String>, line: i32, col: i32) -> Self {
Self::with_message_and_cause_at(message, None, line, col)
}
#[must_use]
pub fn with_cause_at(cause: Option<RawParseCause>, line: i32, col: i32) -> Self {
Self {
message: Some(message_prefix(line, col)),
line: Some(line),
col: Some(col),
cause,
}
}
#[must_use]
pub fn get_message(&self) -> Option<&Utf16String> {
self.message.as_ref()
}
#[must_use]
pub const fn get_line(&self) -> Option<i32> {
self.line
}
#[must_use]
pub const fn get_col(&self) -> Option<i32> {
self.col
}
#[must_use]
pub const fn get_cause(&self) -> Option<&RawParseCause> {
self.cause.as_ref()
}
}
impl Default for RawParseException {
fn default() -> Self {
Self::new()
}
}
impl Display for RawParseException {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter.write_str(
&self
.message
.as_ref()
.map_or_else(|| "null".to_owned(), Utf16String::to_string_lossy),
)
}
}
impl Error for RawParseException {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.cause.as_ref().map(RawParseCause::source_error)
}
}
fn inherited_location(cause: Option<&RawParseCause>) -> (Option<i32>, Option<i32>) {
cause
.and_then(|cause| cause.raw_parse_location.as_ref())
.map_or((None, None), |(line, col, _)| (Some(*line), Some(*col)))
}
fn compose_inherited_message(
message: Option<&Utf16String>,
cause: Option<&RawParseCause>,
) -> Option<Utf16String> {
if let Some(cause) = cause
&& let Some((line, col, cause_message)) = cause.raw_parse_location.as_ref()
{
let mut result = message_prefix(*line, *col).as_utf16().to_vec();
match message {
Some(message) => {
result.push(u16::from(b' '));
result.extend_from_slice(message.as_utf16());
}
None => result.extend_from_slice(cause_message.as_utf16()),
}
return Some(Utf16String::from_utf16(result));
}
if let Some(message) = message {
return Some(message.clone());
}
cause.and_then(|cause| cause.message.clone())
}
fn message_prefix(line: i32, col: i32) -> Utf16String {
Utf16String::from_rust_str(&format!("(Line = {line}, Column = {col})"))
}
fn prefix_with_message(line: i32, col: i32, message: Option<&Utf16String>) -> Utf16String {
let mut result = message_prefix(line, col).as_utf16().to_vec();
result.push(u16::from(b' '));
match message {
Some(message) => result.extend_from_slice(message.as_utf16()),
None => result.extend("null".encode_utf16()),
}
Utf16String::from_utf16(result)
}