use core::fmt;
use std::fmt::{Display, Formatter};
use std::io::{Cursor, Write};
use crate::xdoc::error::{Result, XDocErr};
use crate::xdoc::WriteOpts;
#[derive(Debug, Clone, Eq, PartialOrd, PartialEq, Ord, Hash, Default)]
pub struct Pi {
target: String,
data: String,
}
impl Pi {
pub fn new<S1, S2>(target: S1, data: S2) -> Result<Self>
where
S1: Into<String> + AsRef<str>,
S2: Into<String> + AsRef<str>,
{
if !pi_str_ok(target.as_ref()) {
return Err(XDocErr {
message: format!(
"invalid processing instruction target '{}'",
target.as_ref()
),
file: file!().into(),
line: line!() as u64,
source: None,
});
}
if !pi_str_ok(data.as_ref()) {
return Err(XDocErr {
message: format!("invalid processing instruction data '{}'", target.as_ref()),
file: file!().into(),
line: line!() as u64,
source: None,
});
}
Ok(Self::new_unchecked(target, data))
}
pub fn target(&self) -> &String {
&self.target
}
pub fn data(&self) -> &String {
&self.data
}
pub fn write<W>(&self, writer: &mut W, opts: &WriteOpts, depth: usize) -> Result<()>
where
W: Write,
{
opts.indent(writer, depth)?;
xwrite!(writer, "<?{}", &self.target)?;
if !self.data.is_empty() {
xwrite!(writer, " {}", self.data)?;
}
xwrite!(writer, "?>")?;
Ok(())
}
pub(crate) fn new_unchecked<S1, S2>(target: S1, data: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
Self {
target: target.into(),
data: data.into(),
}
}
}
fn pi_str_ok<S: AsRef<str>>(s: S) -> bool {
!s.as_ref().contains("?>")
}
impl Display for Pi {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut c = Cursor::new(Vec::new());
if self.write(&mut c, &WriteOpts::default(), 0).is_err() {
return write!(f, "<?error?>");
}
let data = c.into_inner();
let data_str = std::str::from_utf8(data.as_slice()).unwrap_or("<?error?>");
write!(f, "{}", data_str)
}
}
#[test]
fn pi_test_simple() {
let pi = Pi::new("thetarget", "dat1 dat2").unwrap();
let got = pi.to_string();
let want = "<?thetarget dat1 dat2?>";
assert_eq!(got, want);
}
#[test]
fn pi_test_empty() {
let pi = Pi::new("x", "").unwrap();
let got = pi.to_string();
let want = "<?x?>";
assert_eq!(got, want);
}
#[test]
fn pi_test_bad() {
let result = Pi::new("x", "da?>t1");
assert!(result.is_err());
}