I'm halfway through the HTML spec.
This commit is contained in:
parent
73344d0dbf
commit
16f0be33a4
|
@ -23,7 +23,7 @@ pub fn global_attrs(span: Span) -> StringyMap<Ident, TokenStream> {
|
|||
insert("autocapitalize", "String");
|
||||
insert("contenteditable", "bool");
|
||||
insert("contextmenu", "crate::types::Id");
|
||||
insert("dir", "String");
|
||||
insert("dir", "crate::types::TextDirection");
|
||||
insert("draggable", "bool");
|
||||
insert("hidden", "bool");
|
||||
insert("is", "String");
|
||||
|
|
|
@ -26,7 +26,7 @@ fn main() {
|
|||
<body>
|
||||
<h1 data-lol="omg" data-foo=wibble.foo>"Hello Kitty!"</h1>
|
||||
<p class=splain_class>
|
||||
"She is not a "<em>"cat"</em>". She is a "<em>"human girl"</em>"."
|
||||
"She is not a "<em><a href="https://en.wikipedia.org/wiki/Cat">"cat"</a></em>". She is a "<em>"human girl"</em>"."
|
||||
</p>
|
||||
<p class=["foo", "bar"]>{the_big_question}</p>
|
||||
{
|
||||
|
|
|
@ -32,7 +32,17 @@ pub trait Element: Node {
|
|||
|
||||
pub trait MetadataContent: Node {}
|
||||
pub trait FlowContent: Node {}
|
||||
pub trait PhrasingContent: Node {}
|
||||
pub trait SectioningContent: Node {}
|
||||
pub trait HeadingContent: Node {}
|
||||
// Phrasing content seems to be entirely a subclass of FlowContent
|
||||
pub trait PhrasingContent: FlowContent {}
|
||||
pub trait EmbeddedContent: Node {}
|
||||
pub trait InteractiveContent: Node {}
|
||||
pub trait FormContent: Node {}
|
||||
|
||||
// Traits for elements that are more picky about their children
|
||||
pub trait DescriptionListContent: Node {}
|
||||
pub trait HGroupContent: Node {}
|
||||
|
||||
impl IntoIterator for TextNode {
|
||||
type Item = TextNode;
|
||||
|
@ -93,7 +103,7 @@ declare_element!(body {} [] FlowContent);
|
|||
// Metadata content
|
||||
declare_element!(base {
|
||||
href: Uri,
|
||||
target: String,
|
||||
target: Target,
|
||||
} [] [MetadataContent]);
|
||||
declare_element!(link {
|
||||
as: Mime,
|
||||
|
@ -121,15 +131,151 @@ declare_element!(style {
|
|||
declare_element!(title {} [] [MetadataContent] TextNode);
|
||||
|
||||
// Flow content
|
||||
declare_element!(a {
|
||||
download: String,
|
||||
href: Uri,
|
||||
hreflang: LanguageTag,
|
||||
ping: SpacedList<Uri>,
|
||||
rel: SpacedList<LinkType>,
|
||||
target: Target,
|
||||
type: Mime,
|
||||
} [] [FlowContent, PhrasingContent, InteractiveContent] FlowContent);
|
||||
declare_element!(abbr {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(address {} [] [FlowContent] FlowContent); // FIXME it has additional constraints on FlowContent
|
||||
declare_element!(article {} [] [FlowContent, SectioningContent] FlowContent);
|
||||
declare_element!(aside {} [] [FlowContent, SectioningContent] FlowContent);
|
||||
declare_element!(audio {
|
||||
autoplay: bool,
|
||||
controls: bool,
|
||||
crossorigin: CrossOrigin,
|
||||
loop: bool,
|
||||
muted: bool,
|
||||
preload: Preload,
|
||||
src: Uri,
|
||||
} [] [FlowContent, PhrasingContent]);
|
||||
declare_element!(b {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(bdo {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(bdi {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(blockquote {
|
||||
cite: Uri,
|
||||
} [] [FlowContent] FlowContent);
|
||||
declare_element!(br {} [] [FlowContent, PhrasingContent]);
|
||||
declare_element!(button {
|
||||
autofocus: bool,
|
||||
disabled: bool,
|
||||
form: Id,
|
||||
formaction: Uri,
|
||||
formenctype: FormEncodingType,
|
||||
formmethod: FormMethod,
|
||||
formnovalidate: bool,
|
||||
formtarget: Target,
|
||||
name: Id,
|
||||
type: ButtonType,
|
||||
value: String,
|
||||
} [] [FlowContent, PhrasingContent, InteractiveContent, FormContent] PhrasingContent);
|
||||
declare_element!(canvas {
|
||||
height: usize,
|
||||
width: usize,
|
||||
} [] [FlowContent] FlowContent); // FIXME has additional child constraints
|
||||
declare_element!(cite {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(code {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(data {
|
||||
value: String,
|
||||
} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(datalist {} [] [FlowContent, PhrasingContent] Element_option);
|
||||
declare_element!(del {
|
||||
cite: Uri,
|
||||
datetime: String, // FIXME should be "a valid date string with an optional time",
|
||||
// but I have other hells to live in right now.
|
||||
} [] [FlowContent, PhrasingContent] FlowContent);
|
||||
declare_element!(details {
|
||||
open: bool,
|
||||
} [summary] [FlowContent, SectioningContent, InteractiveContent] FlowContent);
|
||||
declare_element!(dfn {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(div {} [] [FlowContent] FlowContent);
|
||||
declare_element!(p {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h1 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h2 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h3 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h4 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h5 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(h6 {} [] [FlowContent] PhrasingContent);
|
||||
declare_element!(dl {} [] [FlowContent] DescriptionListContent);
|
||||
declare_element!(em {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(embed {
|
||||
height: usize,
|
||||
src: Uri,
|
||||
type: Mime,
|
||||
width: usize,
|
||||
} [] [FlowContent, PhrasingContent, EmbeddedContent, InteractiveContent]);
|
||||
// FIXME the legend attribute should be optional
|
||||
declare_element!(fieldset {} [legend] [FlowContent, SectioningContent, FormContent] FlowContent);
|
||||
// FIXME the figcaption attribute should be optional
|
||||
declare_element!(figure {} [figcaption] [FlowContent, SectioningContent] FlowContent);
|
||||
declare_element!(footer {} [] [FlowContent] FlowContent);
|
||||
declare_element!(form {
|
||||
accept-charset: SpacedList<CharacterEncoding>,
|
||||
action: Uri,
|
||||
autocomplete: OnOff,
|
||||
enctype: FormEncodingType,
|
||||
method: FormMethod,
|
||||
name: Id,
|
||||
novalidate: bool,
|
||||
target: Target,
|
||||
} [] [FlowContent] FlowContent);
|
||||
declare_element!(h1 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(h2 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(h3 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(h4 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(h5 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(h6 {} [] [FlowContent, HeadingContent, HGroupContent] PhrasingContent);
|
||||
declare_element!(header {} [] [FlowContent] FlowContent);
|
||||
declare_element!(hgroup {} [] [FlowContent, HeadingContent] HGroupContent);
|
||||
declare_element!(hr {} [] [FlowContent]);
|
||||
declare_element!(i {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
declare_element!(iframe {
|
||||
allow: FeaturePolicy,
|
||||
allowfullscreen: bool,
|
||||
allowpaymentrequest: bool,
|
||||
height: usize,
|
||||
name: Id,
|
||||
referrerpolicy: ReferrerPolicy,
|
||||
sandbox: SpacedSet<Sandbox>,
|
||||
src: Uri,
|
||||
srcdoc: Uri,
|
||||
width: usize,
|
||||
} [] [FlowContent, PhrasingContent, EmbeddedContent, InteractiveContent] FlowContent);
|
||||
declare_element!(img {
|
||||
alt: String,
|
||||
crossorigin: CrossOrigin,
|
||||
decoding: ImageDecoding,
|
||||
height: usize,
|
||||
ismap: bool,
|
||||
sizes: SpacedList<String>, // FIXME it's not really just a string
|
||||
src: Uri,
|
||||
srcset: String, // FIXME this is much more complicated
|
||||
usemap: String, // FIXME should be a fragment starting with '#'
|
||||
width: usize,
|
||||
} [] [FlowContent, PhrasingContent, EmbeddedContent]);
|
||||
declare_element!(input {
|
||||
autocomplete: String,
|
||||
autofocus: bool,
|
||||
disabled: bool,
|
||||
form: Id,
|
||||
list: Id,
|
||||
name: Id,
|
||||
required: bool,
|
||||
tabindex: usize,
|
||||
type: InputType,
|
||||
value: String,
|
||||
} [] [FlowContent, FormContent, PhrasingContent]);
|
||||
declare_element!(p {} [] [FlowContent] PhrasingContent);
|
||||
|
||||
// Non-content elements
|
||||
declare_element!(dd {} [] [DescriptionListContent] FlowContent);
|
||||
declare_element!(dt {} [] [DescriptionListContent] FlowContent);
|
||||
declare_element!(figcaption {} [] [] FlowContent);
|
||||
declare_element!(legend {} [] [] PhrasingContent);
|
||||
declare_element!(option {
|
||||
disabled: bool,
|
||||
label: String,
|
||||
selected: bool,
|
||||
value: String,
|
||||
} [] [] TextNode);
|
||||
declare_element!(summary {} [] [] PhrasingContent);
|
||||
|
||||
// Don't @ me
|
||||
declare_element!(blink {} [] [FlowContent, PhrasingContent] PhrasingContent);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::fmt::{Display, Error, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::Id;
|
||||
|
||||
|
@ -47,6 +48,13 @@ impl Class {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Class {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Class::try_new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Class {
|
||||
type Error = &'static str;
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::Class;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ClassList(BTreeSet<Class>);
|
||||
|
||||
impl ClassList {
|
||||
pub fn new() -> Self {
|
||||
ClassList(BTreeSet::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClassList {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Class> for ClassList {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = Class>,
|
||||
{
|
||||
ClassList(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a Class> for ClassList {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a Class>,
|
||||
{
|
||||
ClassList(iter.into_iter().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<String> for ClassList {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = String>,
|
||||
{
|
||||
ClassList(iter.into_iter().map(Class::new).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a str> for ClassList {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a str>,
|
||||
{
|
||||
ClassList(iter.into_iter().map(Class::new).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ClassList {
|
||||
fn from(s: &'a str) -> Self {
|
||||
Self::from_iter(s.split_whitespace().map(Class::new))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClassList {
|
||||
type Target = BTreeSet<Class>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ClassList {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ClassList {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
let mut it = self.0.iter().peekable();
|
||||
while let Some(class) = it.next() {
|
||||
Display::fmt(class, f)?;
|
||||
if it.peek().is_some() {
|
||||
Display::fmt(" ", f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ClassList {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
f.debug_list().entries(self.0.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list.insert(Class::new(s.3));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list.insert(Class::new(s.3));
|
||||
list.insert(Class::new(s.4));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str, &str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list.insert(Class::new(s.3));
|
||||
list.insert(Class::new(s.4));
|
||||
list.insert(Class::new(s.5));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str, &str, &str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list.insert(Class::new(s.3));
|
||||
list.insert(Class::new(s.4));
|
||||
list.insert(Class::new(s.5));
|
||||
list.insert(Class::new(s.6));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str, &str, &str, &str, &str, &str, &str)> for ClassList {
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(Class::new(s.0));
|
||||
list.insert(Class::new(s.1));
|
||||
list.insert(Class::new(s.2));
|
||||
list.insert(Class::new(s.3));
|
||||
list.insert(Class::new(s.4));
|
||||
list.insert(Class::new(s.5));
|
||||
list.insert(Class::new(s.6));
|
||||
list.insert(Class::new(s.7));
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! classlist_from_array {
|
||||
($num:tt) => {
|
||||
impl From<[&str; $num]> for ClassList {
|
||||
fn from(s: [&str; $num]) -> Self {
|
||||
Self::from_iter(s.into_iter().map(|s| Class::new(*s)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
classlist_from_array!(1);
|
||||
classlist_from_array!(2);
|
||||
classlist_from_array!(3);
|
||||
classlist_from_array!(4);
|
||||
classlist_from_array!(5);
|
||||
classlist_from_array!(6);
|
||||
classlist_from_array!(7);
|
||||
classlist_from_array!(8);
|
||||
classlist_from_array!(9);
|
||||
classlist_from_array!(10);
|
||||
classlist_from_array!(11);
|
||||
classlist_from_array!(12);
|
||||
classlist_from_array!(13);
|
||||
classlist_from_array!(14);
|
||||
classlist_from_array!(15);
|
||||
classlist_from_array!(16);
|
||||
classlist_from_array!(17);
|
||||
classlist_from_array!(18);
|
||||
classlist_from_array!(19);
|
||||
classlist_from_array!(20);
|
||||
classlist_from_array!(21);
|
||||
classlist_from_array!(22);
|
||||
classlist_from_array!(23);
|
||||
classlist_from_array!(24);
|
||||
classlist_from_array!(25);
|
||||
classlist_from_array!(26);
|
||||
classlist_from_array!(27);
|
||||
classlist_from_array!(28);
|
||||
classlist_from_array!(29);
|
||||
classlist_from_array!(30);
|
||||
classlist_from_array!(31);
|
||||
classlist_from_array!(32);
|
|
@ -1,6 +1,7 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::fmt::{Display, Error, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::Class;
|
||||
|
||||
|
@ -42,6 +43,13 @@ impl Id {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Id {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Id::try_new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Id {
|
||||
type Error = &'static str;
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
|
|
|
@ -4,19 +4,126 @@ pub use self::class::Class;
|
|||
mod id;
|
||||
pub use self::id::Id;
|
||||
|
||||
mod classlist;
|
||||
pub use self::classlist::ClassList;
|
||||
mod spacedlist;
|
||||
pub use self::spacedlist::SpacedList;
|
||||
|
||||
mod spacedset;
|
||||
pub use self::spacedset::SpacedSet;
|
||||
|
||||
pub type ClassList = SpacedSet<Class>;
|
||||
|
||||
pub use http::Uri;
|
||||
pub use language_tags::LanguageTag;
|
||||
pub use mime::Mime;
|
||||
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum CrossOrigin {
|
||||
#[strum(to_string = "anonymous")]
|
||||
Anonymous,
|
||||
#[strum(to_string = "use-credentials")]
|
||||
UseCredentials,
|
||||
pub type Target = String;
|
||||
pub type CharacterEncoding = String;
|
||||
pub type FeaturePolicy = String;
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum ButtonType {
|
||||
#[strum(to_string = "submit")]
|
||||
Submit,
|
||||
#[strum(to_string = "reset")]
|
||||
Reset,
|
||||
#[strum(to_string = "button")]
|
||||
Button,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum CrossOrigin {
|
||||
#[strum(to_string = "anonymous")]
|
||||
Anonymous,
|
||||
#[strum(to_string = "use-credentials")]
|
||||
UseCredentials,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum FormEncodingType {
|
||||
#[strum(to_string = "application/x-www-form-urlencoded")]
|
||||
UrlEncoded,
|
||||
#[strum(to_string = "multipart/form-data")]
|
||||
FormData,
|
||||
#[strum(to_string = "text/plain")]
|
||||
Text,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum FormMethod {
|
||||
#[strum(to_string = "post")]
|
||||
Post,
|
||||
#[strum(to_string = "get")]
|
||||
Get,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum ImageDecoding {
|
||||
#[strum(to_string = "sync")]
|
||||
Sync,
|
||||
#[strum(to_string = "async")]
|
||||
Async,
|
||||
#[strum(to_string = "auto")]
|
||||
Auto,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum InputType {
|
||||
#[strum(to_string = "button")]
|
||||
Button,
|
||||
#[strum(to_string = "checkbox")]
|
||||
Checkbox,
|
||||
#[strum(to_string = "color")]
|
||||
Color,
|
||||
#[strum(to_string = "date")]
|
||||
Date,
|
||||
#[strum(to_string = "datetime-local")]
|
||||
DatetimeLocal,
|
||||
#[strum(to_string = "email")]
|
||||
Email,
|
||||
#[strum(to_string = "file")]
|
||||
File,
|
||||
#[strum(to_string = "hidden")]
|
||||
Hidden,
|
||||
#[strum(to_string = "image")]
|
||||
Image,
|
||||
#[strum(to_string = "month")]
|
||||
Month,
|
||||
#[strum(to_string = "number")]
|
||||
Number,
|
||||
#[strum(to_string = "password")]
|
||||
Password,
|
||||
#[strum(to_string = "radio")]
|
||||
Radio,
|
||||
#[strum(to_string = "range")]
|
||||
Range,
|
||||
#[strum(to_string = "reset")]
|
||||
Reset,
|
||||
#[strum(to_string = "search")]
|
||||
Search,
|
||||
#[strum(to_string = "submit")]
|
||||
Submit,
|
||||
#[strum(to_string = "tel")]
|
||||
Tel,
|
||||
#[strum(to_string = "text")]
|
||||
Text,
|
||||
#[strum(to_string = "time")]
|
||||
Time,
|
||||
#[strum(to_string = "url")]
|
||||
Url,
|
||||
#[strum(to_string = "week")]
|
||||
Week,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
|
@ -68,3 +175,79 @@ enum_set_type! {
|
|||
Tag,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum OnOff {
|
||||
#[strum(to_string = "on")]
|
||||
On,
|
||||
#[strum(to_string = "off")]
|
||||
Off,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum Preload {
|
||||
#[strum(to_string = "none")]
|
||||
None,
|
||||
#[strum(to_string = "metadata")]
|
||||
Metadata,
|
||||
#[strum(to_string = "auto")]
|
||||
Auto,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum ReferrerPolicy {
|
||||
#[strum(to_string = "no-referrer")]
|
||||
NoReferrer,
|
||||
#[strum(to_string = "no-referrer-when-downgrade")]
|
||||
NoReferrerWhenDowngrade,
|
||||
#[strum(to_string = "origin")]
|
||||
Origin,
|
||||
#[strum(to_string = "origin-when-cross-origin")]
|
||||
OriginWhenCrossOrigin,
|
||||
#[strum(to_string = "unsafe-url")]
|
||||
UnsafeUrl,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum Sandbox {
|
||||
#[strum(to_string = "allow-forms")]
|
||||
AllowForms,
|
||||
#[strum(to_string = "allow-modals")]
|
||||
AllowModals,
|
||||
#[strum(to_string = "allow-orientation-lock")]
|
||||
AllowOrientationLock,
|
||||
#[strum(to_string = "allow-pointer-lock")]
|
||||
AllowPointerLock,
|
||||
#[strum(to_string = "allow-popups")]
|
||||
AllowPopups,
|
||||
#[strum(to_string = "allow-popups-to-escape-sandbox")]
|
||||
AllowPopupsToEscapeSandbox,
|
||||
#[strum(to_string = "allow-presentation")]
|
||||
AllowPresentation,
|
||||
#[strum(to_string = "allow-same-origin")]
|
||||
AllowSameOrigin,
|
||||
#[strum(to_string = "allow-scripts")]
|
||||
AllowScripts,
|
||||
#[strum(to_string = "allow-top-navigation")]
|
||||
AllowTopNavigation,
|
||||
#[strum(to_string = "allow-top-navigation-by-user-navigation")]
|
||||
AllowTopNavigationByUserNavigation,
|
||||
}
|
||||
}
|
||||
|
||||
enum_set_type! {
|
||||
#[derive(EnumString, Display)]
|
||||
pub enum TextDirection {
|
||||
#[strum(to_string = "ltr")]
|
||||
LeftToRight,
|
||||
#[strum(to_string = "rtl")]
|
||||
RightToLeft,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SpacedList<A>(Vec<A>);
|
||||
|
||||
impl<A> SpacedList<A> {
|
||||
pub fn new() -> Self {
|
||||
SpacedList(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Default for SpacedList<A> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> FromIterator<A> for SpacedList<A> {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = A>,
|
||||
{
|
||||
SpacedList(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: 'a + Clone> FromIterator<&'a A> for SpacedList<A> {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a A>,
|
||||
{
|
||||
SpacedList(iter.into_iter().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: FromStr> From<&'a str> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: &'a str) -> Self {
|
||||
Self::from_iter(s.split_whitespace().map(|s| FromStr::from_str(s).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Deref for SpacedList<A> {
|
||||
type Target = Vec<A>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> DerefMut for SpacedList<A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Display> Display for SpacedList<A> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
let mut it = self.0.iter().peekable();
|
||||
while let Some(class) = it.next() {
|
||||
Display::fmt(class, f)?;
|
||||
if it.peek().is_some() {
|
||||
Display::fmt(" ", f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Debug> Debug for SpacedList<A> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
f.debug_list().entries(self.0.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list.push(FromStr::from_str(s.3).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list.push(FromStr::from_str(s.3).unwrap());
|
||||
list.push(FromStr::from_str(s.4).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str, &str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list.push(FromStr::from_str(s.3).unwrap());
|
||||
list.push(FromStr::from_str(s.4).unwrap());
|
||||
list.push(FromStr::from_str(s.5).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str, &str, &str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list.push(FromStr::from_str(s.3).unwrap());
|
||||
list.push(FromStr::from_str(s.4).unwrap());
|
||||
list.push(FromStr::from_str(s.5).unwrap());
|
||||
list.push(FromStr::from_str(s.6).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: FromStr> From<(&str, &str, &str, &str, &str, &str, &str, &str)> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.push(FromStr::from_str(s.0).unwrap());
|
||||
list.push(FromStr::from_str(s.1).unwrap());
|
||||
list.push(FromStr::from_str(s.2).unwrap());
|
||||
list.push(FromStr::from_str(s.3).unwrap());
|
||||
list.push(FromStr::from_str(s.4).unwrap());
|
||||
list.push(FromStr::from_str(s.5).unwrap());
|
||||
list.push(FromStr::from_str(s.6).unwrap());
|
||||
list.push(FromStr::from_str(s.7).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! spacedlist_from_array {
|
||||
($num:tt) => {
|
||||
impl<A: FromStr> From<[&str; $num]> for SpacedList<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: [&str; $num]) -> Self {
|
||||
Self::from_iter(s.into_iter().map(|s| FromStr::from_str(*s).unwrap()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
spacedlist_from_array!(1);
|
||||
spacedlist_from_array!(2);
|
||||
spacedlist_from_array!(3);
|
||||
spacedlist_from_array!(4);
|
||||
spacedlist_from_array!(5);
|
||||
spacedlist_from_array!(6);
|
||||
spacedlist_from_array!(7);
|
||||
spacedlist_from_array!(8);
|
||||
spacedlist_from_array!(9);
|
||||
spacedlist_from_array!(10);
|
||||
spacedlist_from_array!(11);
|
||||
spacedlist_from_array!(12);
|
||||
spacedlist_from_array!(13);
|
||||
spacedlist_from_array!(14);
|
||||
spacedlist_from_array!(15);
|
||||
spacedlist_from_array!(16);
|
||||
spacedlist_from_array!(17);
|
||||
spacedlist_from_array!(18);
|
||||
spacedlist_from_array!(19);
|
||||
spacedlist_from_array!(20);
|
||||
spacedlist_from_array!(21);
|
||||
spacedlist_from_array!(22);
|
||||
spacedlist_from_array!(23);
|
||||
spacedlist_from_array!(24);
|
||||
spacedlist_from_array!(25);
|
||||
spacedlist_from_array!(26);
|
||||
spacedlist_from_array!(27);
|
||||
spacedlist_from_array!(28);
|
||||
spacedlist_from_array!(29);
|
||||
spacedlist_from_array!(30);
|
||||
spacedlist_from_array!(31);
|
||||
spacedlist_from_array!(32);
|
|
@ -0,0 +1,229 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SpacedSet<A: Ord>(BTreeSet<A>);
|
||||
|
||||
impl<A: Ord> SpacedSet<A> {
|
||||
pub fn new() -> Self {
|
||||
SpacedSet(BTreeSet::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> Default for SpacedSet<A> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> FromIterator<A> for SpacedSet<A> {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = A>,
|
||||
{
|
||||
SpacedSet(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: 'a + Ord + Clone> FromIterator<&'a A> for SpacedSet<A> {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a A>,
|
||||
{
|
||||
SpacedSet(iter.into_iter().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Ord + FromStr> From<&'a str> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: &'a str) -> Self {
|
||||
Self::from_iter(s.split_whitespace().map(|s| FromStr::from_str(s).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> Deref for SpacedSet<A> {
|
||||
type Target = BTreeSet<A>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord> DerefMut for SpacedSet<A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + Display> Display for SpacedSet<A> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
let mut it = self.0.iter().peekable();
|
||||
while let Some(class) = it.next() {
|
||||
Display::fmt(class, f)?;
|
||||
if it.peek().is_some() {
|
||||
Display::fmt(" ", f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + Debug> Debug for SpacedSet<A> {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
f.debug_list().entries(self.0.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list.insert(FromStr::from_str(s.3).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list.insert(FromStr::from_str(s.3).unwrap());
|
||||
list.insert(FromStr::from_str(s.4).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str, &str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list.insert(FromStr::from_str(s.3).unwrap());
|
||||
list.insert(FromStr::from_str(s.4).unwrap());
|
||||
list.insert(FromStr::from_str(s.5).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str, &str, &str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list.insert(FromStr::from_str(s.3).unwrap());
|
||||
list.insert(FromStr::from_str(s.4).unwrap());
|
||||
list.insert(FromStr::from_str(s.5).unwrap());
|
||||
list.insert(FromStr::from_str(s.6).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Ord + FromStr> From<(&str, &str, &str, &str, &str, &str, &str, &str)> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.insert(FromStr::from_str(s.0).unwrap());
|
||||
list.insert(FromStr::from_str(s.1).unwrap());
|
||||
list.insert(FromStr::from_str(s.2).unwrap());
|
||||
list.insert(FromStr::from_str(s.3).unwrap());
|
||||
list.insert(FromStr::from_str(s.4).unwrap());
|
||||
list.insert(FromStr::from_str(s.5).unwrap());
|
||||
list.insert(FromStr::from_str(s.6).unwrap());
|
||||
list.insert(FromStr::from_str(s.7).unwrap());
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! spacedlist_from_array {
|
||||
($num:tt) => {
|
||||
impl<A: Ord + FromStr> From<[&str; $num]> for SpacedSet<A>
|
||||
where
|
||||
<A as FromStr>::Err: Debug,
|
||||
{
|
||||
fn from(s: [&str; $num]) -> Self {
|
||||
Self::from_iter(s.into_iter().map(|s| FromStr::from_str(*s).unwrap()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
spacedlist_from_array!(1);
|
||||
spacedlist_from_array!(2);
|
||||
spacedlist_from_array!(3);
|
||||
spacedlist_from_array!(4);
|
||||
spacedlist_from_array!(5);
|
||||
spacedlist_from_array!(6);
|
||||
spacedlist_from_array!(7);
|
||||
spacedlist_from_array!(8);
|
||||
spacedlist_from_array!(9);
|
||||
spacedlist_from_array!(10);
|
||||
spacedlist_from_array!(11);
|
||||
spacedlist_from_array!(12);
|
||||
spacedlist_from_array!(13);
|
||||
spacedlist_from_array!(14);
|
||||
spacedlist_from_array!(15);
|
||||
spacedlist_from_array!(16);
|
||||
spacedlist_from_array!(17);
|
||||
spacedlist_from_array!(18);
|
||||
spacedlist_from_array!(19);
|
||||
spacedlist_from_array!(20);
|
||||
spacedlist_from_array!(21);
|
||||
spacedlist_from_array!(22);
|
||||
spacedlist_from_array!(23);
|
||||
spacedlist_from_array!(24);
|
||||
spacedlist_from_array!(25);
|
||||
spacedlist_from_array!(26);
|
||||
spacedlist_from_array!(27);
|
||||
spacedlist_from_array!(28);
|
||||
spacedlist_from_array!(29);
|
||||
spacedlist_from_array!(30);
|
||||
spacedlist_from_array!(31);
|
||||
spacedlist_from_array!(32);
|
Loading…
Reference in New Issue