#![no_std]
#![doc(html_root_url = "https://docs.rs/parse-author/0.1.0")]
extern crate alloc;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadmeDoctests;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Author {
pub name: Option<String>,
pub email: Option<String>,
pub url: Option<String>,
}
#[must_use]
pub fn parse_author(input: &str) -> Author {
let mut author = Author::default();
if !input
.bytes()
.any(|b| b.is_ascii_alphanumeric() || b == b'_')
{
return author;
}
let chars: Vec<char> = input.chars().collect();
let len = chars.len();
let first_delim = chars.iter().position(|&c| c == '<' || c == '(');
let name = match first_delim {
None => input.trim_matches(is_js_whitespace).to_string(),
Some(k) => chars[..k]
.iter()
.collect::<String>()
.trim_matches(is_js_whitespace)
.to_string(),
};
let mut delims: Vec<(char, String)> = Vec::new();
if let Some(start) = first_delim {
let mut i = start;
let mut count = 0usize;
loop {
if i >= len {
break;
}
let open = chars[i];
if open != '<' && open != '(' {
return author; }
i += 1;
let content_start = i;
while i < len && chars[i] != '>' && chars[i] != ')' {
i += 1;
}
if i >= len {
return author; }
delims.push((open, chars[content_start..i].iter().collect()));
i += 1; count += 1;
let ws_start = i;
while i < len && is_js_whitespace(chars[i]) {
i += 1;
}
if i >= len {
break; }
if count >= 2 && i > ws_start {
return author;
}
}
}
if !name.is_empty() {
author.name = Some(name);
}
if let Some((open, content)) = delims.first() {
assign(&mut author, *open, content);
}
if delims.len() >= 2 {
if let Some((open, content)) = delims.last() {
assign(&mut author, *open, content);
}
}
author
}
fn is_js_whitespace(c: char) -> bool {
(c.is_whitespace() && c != '\u{0085}') || c == '\u{feff}'
}
fn assign(author: &mut Author, open: char, content: &str) {
if content.is_empty() {
return;
}
match open {
'<' => author.email = Some(content.to_string()),
'(' => author.url = Some(content.to_string()),
_ => {}
}
}