I'm halfway through the HTML spec.

This commit is contained in:
Bodil Stokke 2018-11-12 23:20:15 +00:00
parent 73344d0dbf
commit 16f0be33a4
9 changed files with 821 additions and 240 deletions

View File

@ -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");

View File

@ -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>
{

View File

@ -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);

View File

@ -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> {

View File

@ -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);

View File

@ -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> {

View File

@ -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,
}
}

View File

@ -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);

View File

@ -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);