use crate::error::DocsError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Client {
Load,
Visible,
Idle,
}
impl Client {
#[must_use]
pub fn parse(src: &str) -> Client {
match src {
"load" => Client::Load,
"idle" => Client::Idle,
_ => Client::Visible,
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Client::Load => "load",
Client::Visible => "visible",
Client::Idle => "idle",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Island {
pub name: String,
pub client: Client,
pub props: String,
}
impl Island {
pub fn parse(info: &str, body: &str) -> Result<Island, DocsError> {
let mut words = info.split_whitespace();
let keyword = words.next().unwrap_or("");
if keyword != "island" {
return Err(DocsError::IslandWithoutName(info.to_owned()));
}
let mut name = String::new();
let mut client = Client::Visible;
for word in words {
match word.split_once('=') {
Some(("client", value)) => client = Client::parse(value),
Some(_) => {}
None => name = word.to_owned(),
}
}
if name.is_empty() {
return Err(DocsError::IslandWithoutName(info.to_owned()));
}
let props = body.trim();
if !props.is_empty() {
let first = props.chars().next();
let last = props.chars().last();
if first != Some('{') || last != Some('}') {
return Err(DocsError::InvalidIslandProps(props.to_owned()));
}
}
Ok(Island {
name,
client,
props: if props.is_empty() {
"{}".to_owned()
} else {
props.to_owned()
},
})
}
#[must_use]
pub fn to_html(&self, base: &str) -> String {
let component_url = format!("{}/islands/{}.js", base.trim_end_matches('/'), self.name);
format!(
"<docs-island component-url=\"{component_url}\" client=\"{client}\"><script type=\"application/json\" data-island-props>{props}</script></docs-island>",
client = self.client.as_str(),
props = escape_json_for_html(&self.props),
)
}
}
fn escape_json_for_html(src: &str) -> String {
src.replace('<', "\\u003c")
}