use std::fmt::{Display, Formatter};
use std::io;
use std::sync::Arc;
use crate::model::{IModelVisitor, IProcessingInstruction, ITemplateEvent};
use crate::util::{TemplateWriter, Utf16String};
use super::{AbstractTemplateEvent, IEngineTemplateEvent, ITemplateHandler};
pub struct ProcessingInstruction {
template_event: AbstractTemplateEvent,
target: Option<Utf16String>,
content: Option<Utf16String>,
processing_instruction: Utf16String,
}
impl ProcessingInstruction {
#[must_use]
pub fn new(target: Option<Utf16String>, content: Option<Utf16String>) -> Self {
let processing_instruction =
compute_processing_instruction(target.as_ref(), content.as_ref());
Self {
template_event: AbstractTemplateEvent::new(),
target,
content,
processing_instruction,
}
}
#[must_use]
pub fn with_location(
processing_instruction: Option<Utf16String>,
target: Option<Utf16String>,
content: Option<Utf16String>,
template_name: Option<Utf16String>,
line: i32,
col: i32,
) -> Self {
let processing_instruction = processing_instruction
.unwrap_or_else(|| compute_processing_instruction(target.as_ref(), content.as_ref()));
Self {
template_event: AbstractTemplateEvent::with_location(template_name, line, col),
target,
content,
processing_instruction,
}
}
}
impl IProcessingInstruction for ProcessingInstruction {
fn get_target(&self) -> Option<&Utf16String> {
self.target.as_ref()
}
fn get_content(&self) -> Option<&Utf16String> {
self.content.as_ref()
}
fn get_processing_instruction(&self) -> Option<&Utf16String> {
Some(&self.processing_instruction)
}
}
impl ITemplateEvent for ProcessingInstruction {
fn has_location(&self) -> bool {
self.template_event.has_location()
}
fn get_template_name(&self) -> Option<&Utf16String> {
self.template_event.get_template_name()
}
fn get_line(&self) -> i32 {
self.template_event.get_line()
}
fn get_col(&self) -> i32 {
self.template_event.get_col()
}
fn accept(&self, visitor: &mut dyn IModelVisitor) {
visitor.visit_processing_instruction(self);
}
fn be_handled(
self: Arc<Self>,
handler: &mut dyn ITemplateHandler,
) -> Result<(), Box<dyn crate::exceptions::TemplateEngineException>> {
handler.handle_processing_instruction(self)
}
fn write(&self, writer: &mut dyn TemplateWriter) -> io::Result<()> {
writer.write_utf16(self.processing_instruction.as_utf16())
}
}
impl IEngineTemplateEvent for ProcessingInstruction {}
impl Display for ProcessingInstruction {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.processing_instruction.to_string_lossy())
}
}
fn compute_processing_instruction(
target: Option<&Utf16String>,
content: Option<&Utf16String>,
) -> Utf16String {
let mut result = Vec::with_capacity(100);
result.extend("<?".encode_utf16());
append_nullable(&mut result, target);
if let Some(content) = content {
result.push(u16::from(b' '));
result.extend_from_slice(content.as_utf16());
}
result.extend("?>".encode_utf16());
Utf16String::from_utf16(result)
}
fn append_nullable(result: &mut Vec<u16>, value: Option<&Utf16String>) {
match value {
Some(value) => result.extend_from_slice(value.as_utf16()),
None => result.extend("null".encode_utf16()),
}
}