use std::io::{Cursor, Read};
use super::{ITemplateResource, TemplateResourceError};
use crate::TemplateInputException;
pub struct StringTemplateResource {
resource: String,
}
impl StringTemplateResource {
pub fn new(resource: Option<&str>) -> Result<Self, TemplateResourceError> {
let resource = resource.ok_or_else(|| {
TemplateResourceError::InvalidArgument("Resource cannot be null or empty".to_owned())
})?;
Ok(Self {
resource: resource.to_owned(),
})
}
}
impl ITemplateResource for StringTemplateResource {
fn get_description(&self) -> String {
self.resource.clone()
}
fn get_base_name(&self) -> Option<String> {
None
}
fn exists(&self) -> bool {
true
}
fn reader(&self) -> std::io::Result<Box<dyn Read>> {
Ok(Box::new(Cursor::new(self.resource.as_bytes().to_owned())))
}
fn relative(
&self,
relative_location: Option<&str>,
) -> Result<Box<dyn ITemplateResource>, TemplateResourceError> {
let message = match relative_location {
Some(_) | None => format!(
"Cannot create a relative resource for String resource \"{}\"",
self.resource
),
};
Err(TemplateResourceError::Input(TemplateInputException::new(
Some(message),
)))
}
}
#[cfg(test)]
mod tests {
use std::io::Read;
use super::StringTemplateResource;
use crate::templateresource::{ITemplateResource, TemplateResourceError};
#[test]
fn validates_only_null_and_accepts_empty_or_unicode_content() {
let error = StringTemplateResource::new(None)
.err()
.expect("null resource must fail");
assert_eq!(error.to_string(), "Resource cannot be null or empty");
let empty = StringTemplateResource::new(Some("")).expect("empty resource is legal");
assert_eq!(empty.get_description(), "");
assert!(empty.exists());
assert_eq!(empty.get_base_name(), None);
let unicode =
StringTemplateResource::new(Some("你好 😀\n")).expect("unicode resource is legal");
assert_eq!(unicode.get_description(), "你好 😀\n");
}
#[test]
fn creates_independent_readers_from_the_resource_start() {
let resource = StringTemplateResource::new(Some("first\n第二 😀")).expect("valid resource");
let mut first = resource.reader().expect("first reader");
let mut prefix = [0_u8; 5];
first.read_exact(&mut prefix).expect("read prefix");
assert_eq!(&prefix, b"first");
let mut second = resource.reader().expect("second reader");
let mut complete = String::new();
second
.read_to_string(&mut complete)
.expect("read complete resource");
assert_eq!(complete, "first\n第二 😀");
}
#[test]
fn rejects_every_relative_location_with_the_exact_input_exception() {
let resource =
StringTemplateResource::new(Some("line1\n\"line2\"")).expect("valid resource");
for relative_location in [None, Some(""), Some("child.html")] {
let error = resource
.relative(relative_location)
.err()
.expect("string resources never support relatives");
assert_eq!(
error.to_string(),
"Cannot create a relative resource for String resource \"line1\n\"line2\"\""
);
let invalid_argument = TemplateResourceError::InvalidArgument(String::new());
assert_ne!(
std::mem::discriminant(&error),
std::mem::discriminant(&invalid_argument)
);
}
}
}