<commit_before>pub mod dummy;
pub mod full;
pub mod stack_trace;

use super::{heap::Pointer, ChannelId, FiberId, Heap};
use crate::{compiler::hir::Id, module::Module};

/// An event that happened inside a VM.
#[derive(Clone)]
pub enum VmEvent<'event> {
    FiberCreated {
        fiber: FiberId,
    },
    FiberDone {
        fiber: FiberId,
    },
    FiberPanicked {
        fiber: FiberId,
        panicked_child: Option<FiberId>,
    },
    FiberCanceled {
        fiber: FiberId,
    },
    FiberExecutionStarted {
        fiber: FiberId,
    },
    FiberExecutionEnded {
        fiber: FiberId,
    },
    ChannelCreated {
        channel: ChannelId,
    },
    InFiber {
        fiber: FiberId,
        event: FiberEvent<'event>,
    },
}

/// An event that happened inside a fiber.
#[derive(Clone)]
pub enum FiberEvent<'event> {
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
        heap: &'event Heap,
    },
    ValueEvaluated {
        id: Id,
        value: Pointer,
        heap: &'event Heap,
    },
    FoundFuzzableClosure {
        id: Id,
        closure: Pointer,
        heap: &'event Heap,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
        heap: &'event Heap,
    },
    CallEnded {
        return_value: Pointer,
        heap: &'event Heap,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
        heap: &'event Heap,
    },
    NeedsEnded,
}

pub trait Tracer {
    fn add(&mut self, event: VmEvent);

    fn for_vm<'a, 'vm>(&'a mut self) -> VmTracer<'vm>
    where
        Self: Sized,
        'a: 'vm,
    {
        VmTracer::<'vm> { tracer: self }
    }
}
pub struct VmTracer<'vm> {
    tracer: &'vm mut dyn Tracer,
}
pub struct FiberTracer<'vm, 'fiber> {
    vm_tracer: &'fiber mut VmTracer<'vm>,
    fiber: FiberId,
}

impl<'vm> VmTracer<'vm> {
    pub fn for_fiber<'fiber>(&'fiber mut self, fiber: FiberId) -> FiberTracer<'vm, 'fiber>
    where
        Self: 'fiber,
    {
        FiberTracer {
            vm_tracer: self,
            fiber,
        }
    }
}

impl<'vm> VmTracer<'vm> {
    fn add(&mut self, event: VmEvent) {
        self.tracer.add(event);
    }
}
impl<'vm, 'fiber> FiberTracer<'vm, 'fiber> {
    fn add(&mut self, event: FiberEvent) {
        self.vm_tracer.add(VmEvent::InFiber {
            fiber: self.fiber,
            event,
        });
    }
}

impl<'vm> VmTracer<'vm> {
    pub fn fiber_created(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberCreated { fiber });
    }
    pub fn fiber_done(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberDone { fiber });
    }
    pub fn fiber_panicked(&mut self, fiber: FiberId, panicked_child: Option<FiberId>) {
        self.add(VmEvent::FiberPanicked {
            fiber,
            panicked_child,
        });
    }
    pub fn fiber_canceled(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberCanceled { fiber });
    }
    pub fn fiber_execution_started(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberExecutionStarted { fiber });
    }
    pub fn fiber_execution_ended(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberExecutionEnded { fiber });
    }
    pub fn channel_created(&mut self, channel: ChannelId) {
        self.add(VmEvent::ChannelCreated { channel });
    }
}
impl<'vm, 'fiber> FiberTracer<'vm, 'fiber> {
    pub fn module_started(&mut self, module: Module) {
        self.add(FiberEvent::ModuleStarted { module });
    }
    pub fn module_ended(&mut self, export_map: Pointer, heap: &Heap) {
        self.add(FiberEvent::ModuleEnded { export_map, heap });
    }
    pub fn value_evaluated(&mut self, id: Id, value: Pointer, heap: &Heap) {
        self.add(FiberEvent::ValueEvaluated { id, value, heap });
    }
    pub fn found_fuzzable_closure(&mut self, id: Id, closure: Pointer, heap: &Heap) {
        self.add(FiberEvent::FoundFuzzableClosure { id, closure, heap });
    }
    pub fn call_started(&mut self, id: Id, closure: Pointer, args: Vec<Pointer>, heap: &Heap) {
        self.add(FiberEvent::CallStarted {
            id,
            closure,
            args,
            heap,
        });
    }
    pub fn call_ended(&mut self, return_value: Pointer, heap: &Heap) {
        self.add(FiberEvent::CallEnded { return_value, heap });
    }
    pub fn needs_started(&mut self, id: Id, condition: Pointer, reason: Pointer, heap: &Heap) {
        self.add(FiberEvent::NeedsStarted {
            id,
            condition,
            reason,
            heap,
        });
    }
    pub fn needs_ended(&mut self) {
        self.add(FiberEvent::NeedsEnded);
    }
}
<commit_msg>Update compiler/src/vm/tracer/mod.rs<commit_after>pub mod dummy;
pub mod full;
pub mod stack_trace;

use super::{heap::Pointer, ChannelId, FiberId, Heap};
use crate::{compiler::hir::Id, module::Module};

/// An event that happened inside a VM.
#[derive(Clone)]
pub enum VmEvent<'event> {
    FiberCreated {
        fiber: FiberId,
    },
    FiberDone {
        fiber: FiberId,
    },
    FiberPanicked {
        fiber: FiberId,
        panicked_child: Option<FiberId>,
    },
    FiberCanceled {
        fiber: FiberId,
    },
    FiberExecutionStarted {
        fiber: FiberId,
    },
    FiberExecutionEnded {
        fiber: FiberId,
    },
    ChannelCreated {
        channel: ChannelId,
    },
    InFiber {
        fiber: FiberId,
        event: FiberEvent<'event>,
    },
}

/// An event that happened inside a fiber.
#[derive(Clone)]
pub enum FiberEvent<'event> {
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
        heap: &'event Heap,
    },
    ValueEvaluated {
        id: Id,
        value: Pointer,
        heap: &'event Heap,
    },
    FoundFuzzableClosure {
        id: Id,
        closure: Pointer,
        heap: &'event Heap,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
        heap: &'event Heap,
    },
    CallEnded {
        return_value: Pointer,
        heap: &'event Heap,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
        heap: &'event Heap,
    },
    NeedsEnded,
}

pub trait Tracer {
    fn add(&mut self, event: VmEvent);

    fn for_vm<'a, 'vm>(&'a mut self) -> VmTracer<'vm>
    where
        Self: Sized,
        'a: 'vm,
    {
        VmTracer::<'vm> { tracer: self }
    }
}
pub struct VmTracer<'vm> {
    tracer: &'vm mut dyn Tracer,
}
pub struct FiberTracer<'vm, 'fiber> {
    vm_tracer: &'fiber mut VmTracer<'vm>,
    fiber: FiberId,
}

impl<'vm> VmTracer<'vm> {
    pub fn for_fiber<'fiber>(&'fiber mut self, fiber: FiberId) -> FiberTracer<'vm, 'fiber>
    where
        Self: 'fiber,
    {
        FiberTracer {
            vm_tracer: self,
            fiber,
        }
    }
}

impl<'vm> Tracer for VmTracer<'vm> {
    fn add(&mut self, event: VmEvent) {
        self.tracer.add(event);
    }
}
impl<'vm, 'fiber> FiberTracer<'vm, 'fiber> {
    fn add(&mut self, event: FiberEvent) {
        self.vm_tracer.add(VmEvent::InFiber {
            fiber: self.fiber,
            event,
        });
    }
}

impl<'vm> VmTracer<'vm> {
    pub fn fiber_created(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberCreated { fiber });
    }
    pub fn fiber_done(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberDone { fiber });
    }
    pub fn fiber_panicked(&mut self, fiber: FiberId, panicked_child: Option<FiberId>) {
        self.add(VmEvent::FiberPanicked {
            fiber,
            panicked_child,
        });
    }
    pub fn fiber_canceled(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberCanceled { fiber });
    }
    pub fn fiber_execution_started(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberExecutionStarted { fiber });
    }
    pub fn fiber_execution_ended(&mut self, fiber: FiberId) {
        self.add(VmEvent::FiberExecutionEnded { fiber });
    }
    pub fn channel_created(&mut self, channel: ChannelId) {
        self.add(VmEvent::ChannelCreated { channel });
    }
}
impl<'vm, 'fiber> FiberTracer<'vm, 'fiber> {
    pub fn module_started(&mut self, module: Module) {
        self.add(FiberEvent::ModuleStarted { module });
    }
    pub fn module_ended(&mut self, export_map: Pointer, heap: &Heap) {
        self.add(FiberEvent::ModuleEnded { export_map, heap });
    }
    pub fn value_evaluated(&mut self, id: Id, value: Pointer, heap: &Heap) {
        self.add(FiberEvent::ValueEvaluated { id, value, heap });
    }
    pub fn found_fuzzable_closure(&mut self, id: Id, closure: Pointer, heap: &Heap) {
        self.add(FiberEvent::FoundFuzzableClosure { id, closure, heap });
    }
    pub fn call_started(&mut self, id: Id, closure: Pointer, args: Vec<Pointer>, heap: &Heap) {
        self.add(FiberEvent::CallStarted {
            id,
            closure,
            args,
            heap,
        });
    }
    pub fn call_ended(&mut self, return_value: Pointer, heap: &Heap) {
        self.add(FiberEvent::CallEnded { return_value, heap });
    }
    pub fn needs_started(&mut self, id: Id, condition: Pointer, reason: Pointer, heap: &Heap) {
        self.add(FiberEvent::NeedsStarted {
            id,
            condition,
            reason,
            heap,
        });
    }
    pub fn needs_ended(&mut self) {
        self.add(FiberEvent::NeedsEnded);
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens ( & self , tokens : & mut TokenStream ) { ",
        "( * * self ) . to_tokens ( tokens ) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > ( X ) [ X ] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > ( & self , s : & mut S ) -> Result < ( ) , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with ( self . value , s ) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "- 1i8 - 1i16 - 1i32 - 1i64 - 1i128 - 1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_btreeset_repetition() {
    let mut set = BTreeSet::new();
    set.insert("a".to_owned());
    set.insert("b".to_owned());

    let tokens = quote! {
        #(#set: #set),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());
    let id5 = format_ident!("Hello{}", Cow::Borrowed("World"));

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
    assert_eq!(id5, "HelloWorld");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [ inline ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [ no_std ]";
    assert_eq!(expected, tokens.to_string());
}

// https://github.com/dtolnay/quote/issues/130
#[test]
fn test_star_after_repetition() {
    let c = vec!['0', '1'];
    let tokens = quote! {
        #(
            f(#c);
        )*
        *out = None;
    };
    let expected = "f ( '0' ) ; f ( '1' ) ; * out = None ;";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_raw_id() {
    let id = quote!(r#raw_id);
    assert_eq!(id.to_string(), "r#raw_id");
}
<commit_msg>Add quote_spanned smoke test to test suite<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens ( & self , tokens : & mut TokenStream ) { ",
        "( * * self ) . to_tokens ( tokens ) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_spanned_impl() {
    let span = Span::call_site();
    let tokens = quote_spanned! {span=>
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens ( & self , tokens : & mut TokenStream ) { ",
        "( * * self ) . to_tokens ( tokens ) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > ( X ) [ X ] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > ( & self , s : & mut S ) -> Result < ( ) , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with ( self . value , s ) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "- 1i8 - 1i16 - 1i32 - 1i64 - 1i128 - 1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_btreeset_repetition() {
    let mut set = BTreeSet::new();
    set.insert("a".to_owned());
    set.insert("b".to_owned());

    let tokens = quote! {
        #(#set: #set),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());
    let id5 = format_ident!("Hello{}", Cow::Borrowed("World"));

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
    assert_eq!(id5, "HelloWorld");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [ doc = r\" doc\" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [ doc = r\" doc \" ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [ inline ]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [ no_std ]";
    assert_eq!(expected, tokens.to_string());
}

// https://github.com/dtolnay/quote/issues/130
#[test]
fn test_star_after_repetition() {
    let c = vec!['0', '1'];
    let tokens = quote! {
        #(
            f(#c);
        )*
        *out = None;
    };
    let expected = "f ( '0' ) ; f ( '1' ) ; * out = None ;";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_raw_id() {
    let id = quote!(r#raw_id);
    assert_eq!(id.to_string(), "r#raw_id");
}
<|endoftext|>


<commit_before>extern crate postgres;
extern crate r2d2;
extern crate r2d2_postgres;

use std::default::Default;
use std::sync::{Arc, Future};
use std::comm;

use postgres::NoSsl;
use postgres::error::{SocketError, InvalidUrl};
use r2d2_postgres::{PostgresPoolManager, ConnectError};

#[test]
fn test_bad_url_deferred() {
    let manager = PostgresPoolManager::new("not a url", NoSsl);
    let config = Default::default();
    let handler = r2d2::NoopErrorHandler;
    match r2d2::Pool::new(config, manager, handler) {
        Err(r2d2::ConnectionError(ConnectError(InvalidUrl(_)))) => {}
        Err(err) => fail!("Unexpected error {}", err),
        _ => fail!("Unexpected success"),
    }
}

#[test]
fn test_bad_host_error() {
    let manager = PostgresPoolManager::new("postgres://bogushost", NoSsl);
    let config = Default::default();
    let handler = r2d2::NoopErrorHandler;
    match r2d2::Pool::new(config, manager, handler) {
        Err(r2d2::ConnectionError(ConnectError(SocketError(_)))) => {}
        Err(err) => fail!("Unexpected error {}", err),
        _ => fail!("Unexpected success")
    }
}

#[test]
fn test_basic() {
    let manager = PostgresPoolManager::new("postgres://postgres@localhost", NoSsl);
    let config = r2d2::Config {
        pool_size: 2,
        ..Default::default()
    };
    let handler = r2d2::NoopErrorHandler;
    let pool = Arc::new(r2d2::Pool::new(config, manager, handler).unwrap());

    let (s1, r1) = comm::channel();
    let (s2, r2) = comm::channel();

    let pool1 = pool.clone();
    let mut fut1 = Future::spawn(proc() {
        let conn = pool1.get().unwrap();
        s1.send(());
        r2.recv();
        drop(conn);
    });

    let pool2 = pool.clone();
    let mut fut2 = Future::spawn(proc() {
        let conn = pool2.get().unwrap();
        s2.send(());
        r1.recv();
        drop(conn);
    });

    fut1.get();
    fut2.get();

    pool.get().unwrap();
}

#[test]
fn test_is_valid() {
    let manager = PostgresPoolManager::new("postgres://postgres@localhost", NoSsl);
    let config = r2d2::Config {
        pool_size: 1,
        test_on_check_out: true,
        ..Default::default()
    };
    let handler = r2d2::NoopErrorHandler;
    let pool = r2d2::Pool::new(config, manager, handler).unwrap();

    pool.get().unwrap();
}
<commit_msg>Remove test that no longer make sense<commit_after>extern crate postgres;
extern crate r2d2;
extern crate r2d2_postgres;

use std::default::Default;
use std::sync::{Arc, Future};
use std::comm;

use postgres::NoSsl;
use r2d2_postgres::PostgresPoolManager;

#[test]
fn test_basic() {
    let manager = PostgresPoolManager::new("postgres://postgres@localhost", NoSsl);
    let config = r2d2::Config {
        pool_size: 2,
        ..Default::default()
    };
    let handler = r2d2::NoopErrorHandler;
    let pool = Arc::new(r2d2::Pool::new(config, manager, handler).unwrap());

    let (s1, r1) = comm::channel();
    let (s2, r2) = comm::channel();

    let pool1 = pool.clone();
    let mut fut1 = Future::spawn(proc() {
        let conn = pool1.get().unwrap();
        s1.send(());
        r2.recv();
        drop(conn);
    });

    let pool2 = pool.clone();
    let mut fut2 = Future::spawn(proc() {
        let conn = pool2.get().unwrap();
        s2.send(());
        r1.recv();
        drop(conn);
    });

    fut1.get();
    fut2.get();

    pool.get().unwrap();
}

#[test]
fn test_is_valid() {
    let manager = PostgresPoolManager::new("postgres://postgres@localhost", NoSsl);
    let config = r2d2::Config {
        pool_size: 1,
        test_on_check_out: true,
        ..Default::default()
    };
    let handler = r2d2::NoopErrorHandler;
    let pool = r2d2::Pool::new(config, manager, handler).unwrap();

    pool.get().unwrap();
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes

#![deny(missing_docs)]

use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
use parking_lot::RwLock;
use parser::{ParserContext, ParserContextExtraData, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use properties::{PropertyDeclarationId, LonghandId, DeclaredValue};
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use std::fmt;
use std::sync::Arc;
use style_traits::ToCss;
use stylesheets::{MemoryHoleReporter, Stylesheet};

/// A number from 0 to 1, indicating the percentage of the animation when this
/// keyframe should run.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframePercentage(pub f32);

impl ::std::cmp::Ord for KeyframePercentage {
    #[inline]
    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
        // We know we have a number from 0 to 1, so unwrap() here is safe.
        self.0.partial_cmp(&other.0).unwrap()
    }
}

impl ::std::cmp::Eq for KeyframePercentage { }

impl ToCss for KeyframePercentage {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}%", self.0 * 100.0)
    }
}

impl KeyframePercentage {
    /// Trivially constructs a new `KeyframePercentage`.
    #[inline]
    pub fn new(value: f32) -> KeyframePercentage {
        debug_assert!(value >= 0. && value <= 1.);
        KeyframePercentage(value)
    }

    fn parse(input: &mut Parser) -> Result<KeyframePercentage, ()> {
        let percentage = if input.try(|input| input.expect_ident_matching("from")).is_ok() {
            KeyframePercentage::new(0.)
        } else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
            KeyframePercentage::new(1.)
        } else {
            let percentage = try!(input.expect_percentage());
            if percentage >= 0. && percentage <= 1. {
                KeyframePercentage::new(percentage)
            } else {
                return Err(());
            }
        };

        Ok(percentage)
    }
}

/// A keyframes selector is a list of percentages or from/to symbols, which are
/// converted at parse time to percentages.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframeSelector(Vec<KeyframePercentage>);
impl KeyframeSelector {
    /// Return the list of percentages this selector contains.
    #[inline]
    pub fn percentages(&self) -> &[KeyframePercentage] {
        &self.0
    }

    /// A dummy public function so we can write a unit test for this.
    pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
        KeyframeSelector(percentages)
    }

    /// Parse a keyframe selector from CSS input.
    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
        input.parse_comma_separated(KeyframePercentage::parse)
             .map(KeyframeSelector)
    }
}

/// A keyframe.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Keyframe {
    /// The selector this keyframe was specified from.
    pub selector: KeyframeSelector,

    /// The declaration block that was declared inside this keyframe.
    ///
    /// Note that `!important` rules in keyframes don't apply, but we keep this
    /// `Arc` just for convenience.
    #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
    pub block: Arc<RwLock<PropertyDeclarationBlock>>,
}

impl ToCss for Keyframe {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.selector.percentages().iter();
        try!(iter.next().unwrap().to_css(dest));
        for percentage in iter {
            try!(write!(dest, ", "));
            try!(percentage.to_css(dest));
        }
        try!(dest.write_str(" { "));
        try!(self.block.read().to_css(dest));
        try!(dest.write_str(" }"));
        Ok(())
    }
}

impl Keyframe {
    /// Parse a CSS keyframe.
    pub fn parse(css: &str,
                 parent_stylesheet: &Stylesheet,
                 extra_data: ParserContextExtraData)
                 -> Result<Arc<RwLock<Self>>, ()> {
        let error_reporter = Box::new(MemoryHoleReporter);
        let context = ParserContext::new_with_extra_data(parent_stylesheet.origin,
                                                         &parent_stylesheet.base_url,
                                                         error_reporter,
                                                         extra_data);
        let mut input = Parser::new(css);

        let mut rule_parser = KeyframeListParser {
            context: &context,
        };
        parse_one_rule(&mut input, &mut rule_parser)
    }
}

/// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of
/// declarations to apply.
///
/// TODO: Find a better name for this?
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
    /// A step formed by a declaration block specified by the CSS.
    Declarations {
        /// The declaration block per se.
        #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
        block: Arc<RwLock<PropertyDeclarationBlock>>
    },
    /// A synthetic step computed from the current computed values at the time
    /// of the animation.
    ComputedValues,
}

/// A single step from a keyframe animation.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesStep {
    /// The percentage of the animation duration when this step starts.
    pub start_percentage: KeyframePercentage,
    /// Declarations that will determine the final style during the step, or
    /// `ComputedValues` if this is an autogenerated step.
    pub value: KeyframesStepValue,
    /// Wether a animation-timing-function declaration exists in the list of
    /// declarations.
    ///
    /// This is used to know when to override the keyframe animation style.
    pub declared_timing_function: bool,
}

impl KeyframesStep {
    #[inline]
    fn new(percentage: KeyframePercentage,
           value: KeyframesStepValue) -> Self {
        let declared_timing_function = match value {
            KeyframesStepValue::Declarations { ref block } => {
                block.read().declarations.iter().any(|&(ref prop_decl, _)| {
                    match *prop_decl {
                        PropertyDeclaration::AnimationTimingFunction(..) => true,
                        _ => false,
                    }
                })
            }
            _ => false,
        };

        KeyframesStep {
            start_percentage: percentage,
            value: value,
            declared_timing_function: declared_timing_function,
        }
    }

    /// Return specified TransitionTimingFunction if this KeyframesSteps has 'animation-timing-function'.
    pub fn get_animation_timing_function(&self) -> Option<SpecifiedTimingFunction> {
        if !self.declared_timing_function {
            return None;
        }
        match self.value {
            KeyframesStepValue::Declarations { ref block } => {
                let guard = block.read();
                let &(ref declaration, _) =
                    guard.get(PropertyDeclarationId::Longhand(LonghandId::AnimationTimingFunction)).unwrap();
                match *declaration {
                    PropertyDeclaration::AnimationTimingFunction(ref value) => {
                        match *value {
                            DeclaredValue::Value(ref value) => {
                                // Use the first value.
                                Some(value.0[0])
                            },
                            _ => None,
                        }
                    },
                    _ => panic!(),
                }
            },
            KeyframesStepValue::ComputedValues => {
                panic!("Shouldn't happen to set animation-timing-function in missing keyframes")
            },
        }
    }
}

/// This structure represents a list of animation steps computed from the list
/// of keyframes, in order.
///
/// It only takes into account animable properties.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesAnimation {
    /// The difference steps of the animation.
    pub steps: Vec<KeyframesStep>,
    /// The properties that change in this animation.
    pub properties_changed: Vec<TransitionProperty>,
}

/// Get all the animated properties in a keyframes animation. Note that it's not
/// defined what happens when a property is not on a keyframe, so we only peek
/// the props of the first one.
///
/// In practice, browsers seem to try to do their best job at it, so we might
/// want to go through all the actual keyframes and deduplicate properties.
fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
    let mut ret = vec![];
    // NB: declarations are already deduplicated, so we don't have to check for
    // it here.
    for &(ref declaration, _) in keyframe.block.read().declarations.iter() {
        if let Some(property) = TransitionProperty::from_declaration(declaration) {
            ret.push(property);
        }
    }

    ret
}

impl KeyframesAnimation {
    /// Create a keyframes animation from a given list of keyframes.
    ///
    /// This will return a keyframe animation with empty steps and
    /// properties_changed if the list of keyframes is empty, or there are no
    //  animated properties obtained from the keyframes.
    ///
    /// Otherwise, this will compute and sort the steps used for the animation,
    /// and return the animation object.
    pub fn from_keyframes(keyframes: &[Arc<RwLock<Keyframe>>]) -> Self {
        let mut result = KeyframesAnimation {
            steps: vec![],
            properties_changed: vec![],
        };

        if keyframes.is_empty() {
            return result;
        }

        result.properties_changed = get_animated_properties(&keyframes[0].read());
        if result.properties_changed.is_empty() {
            return result;
        }

        for keyframe in keyframes {
            let keyframe = keyframe.read();
            for percentage in keyframe.selector.0.iter() {
                result.steps.push(KeyframesStep::new(*percentage, KeyframesStepValue::Declarations {
                    block: keyframe.block.clone(),
                }));
            }
        }

        // Sort by the start percentage, so we can easily find a frame.
        result.steps.sort_by_key(|step| step.start_percentage);

        // Prepend autogenerated keyframes if appropriate.
        if result.steps[0].start_percentage.0 != 0. {
            result.steps.insert(0, KeyframesStep::new(KeyframePercentage::new(0.),
                                                      KeyframesStepValue::ComputedValues));
        }

        if result.steps.last().unwrap().start_percentage.0 != 1. {
            result.steps.push(KeyframesStep::new(KeyframePercentage::new(1.),
                                                 KeyframesStepValue::ComputedValues));
        }

        result
    }
}

/// Parses a keyframes list, like:
/// 0%, 50% {
///     width: 50%;
/// }
///
/// 40%, 60%, 100% {
///     width: 100%;
/// }
struct KeyframeListParser<'a> {
    context: &'a ParserContext<'a>,
}

/// Parses a keyframe list from CSS input.
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Arc<RwLock<Keyframe>>> {
    RuleListParser::new_for_nested_rule(input, KeyframeListParser { context: context })
        .filter_map(Result::ok)
        .collect()
}

enum Void {}
impl<'a> AtRuleParser for KeyframeListParser<'a> {
    type Prelude = Void;
    type AtRule = Arc<RwLock<Keyframe>>;
}

impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
    type Prelude = KeyframeSelector;
    type QualifiedRule = Arc<RwLock<Keyframe>>;

    fn parse_prelude(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> {
        let start = input.position();
        match KeyframeSelector::parse(input) {
            Ok(sel) => Ok(sel),
            Err(()) => {
                let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
                log_css_error(input, start, &message, self.context);
                Err(())
            }
        }
    }

    fn parse_block(&mut self, prelude: Self::Prelude, input: &mut Parser)
                   -> Result<Self::QualifiedRule, ()> {
        let parser = KeyframeDeclarationParser {
            context: self.context,
            declarations: vec![],
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            match declaration {
                Ok(_) => (),
                Err(range) => {
                    let pos = range.start;
                    let message = format!("Unsupported keyframe property declaration: '{}'",
                                          iter.input.slice(range));
                    log_css_error(iter.input, pos, &*message, self.context);
                }
            }
            // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
        }
        Ok(Arc::new(RwLock::new(Keyframe {
            selector: prelude,
            block: Arc::new(RwLock::new(PropertyDeclarationBlock {
                declarations: iter.parser.declarations,
                important_count: 0,
            })),
        })))
    }
}

struct KeyframeDeclarationParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    declarations: Vec<(PropertyDeclaration, Importance)>
}

/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for KeyframeDeclarationParser<'a, 'b> {
    type Prelude = ();
    type AtRule = ();
}

impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
    /// We parse rules directly into the declarations object
    type Declaration = ();

    fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
        let id = try!(PropertyId::parse(name.into()));
        let old_len = self.declarations.len();
        match PropertyDeclaration::parse(id, self.context, input, &mut self.declarations, true) {
            PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
            _ => {
                self.declarations.truncate(old_len);
                return Err(());
            }
        }
        // In case there is still unparsed text in the declaration, we should roll back.
        if !input.is_exhausted() {
            self.declarations.truncate(old_len);
            Err(())
        } else {
            Ok(())
        }
    }
}
<commit_msg>Assert if there is an important declaration while collecting animated properties in keyframes.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes

#![deny(missing_docs)]

use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
use parking_lot::RwLock;
use parser::{ParserContext, ParserContextExtraData, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use properties::{PropertyDeclarationId, LonghandId, DeclaredValue};
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use std::fmt;
use std::sync::Arc;
use style_traits::ToCss;
use stylesheets::{MemoryHoleReporter, Stylesheet};

/// A number from 0 to 1, indicating the percentage of the animation when this
/// keyframe should run.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframePercentage(pub f32);

impl ::std::cmp::Ord for KeyframePercentage {
    #[inline]
    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
        // We know we have a number from 0 to 1, so unwrap() here is safe.
        self.0.partial_cmp(&other.0).unwrap()
    }
}

impl ::std::cmp::Eq for KeyframePercentage { }

impl ToCss for KeyframePercentage {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}%", self.0 * 100.0)
    }
}

impl KeyframePercentage {
    /// Trivially constructs a new `KeyframePercentage`.
    #[inline]
    pub fn new(value: f32) -> KeyframePercentage {
        debug_assert!(value >= 0. && value <= 1.);
        KeyframePercentage(value)
    }

    fn parse(input: &mut Parser) -> Result<KeyframePercentage, ()> {
        let percentage = if input.try(|input| input.expect_ident_matching("from")).is_ok() {
            KeyframePercentage::new(0.)
        } else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
            KeyframePercentage::new(1.)
        } else {
            let percentage = try!(input.expect_percentage());
            if percentage >= 0. && percentage <= 1. {
                KeyframePercentage::new(percentage)
            } else {
                return Err(());
            }
        };

        Ok(percentage)
    }
}

/// A keyframes selector is a list of percentages or from/to symbols, which are
/// converted at parse time to percentages.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframeSelector(Vec<KeyframePercentage>);
impl KeyframeSelector {
    /// Return the list of percentages this selector contains.
    #[inline]
    pub fn percentages(&self) -> &[KeyframePercentage] {
        &self.0
    }

    /// A dummy public function so we can write a unit test for this.
    pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
        KeyframeSelector(percentages)
    }

    /// Parse a keyframe selector from CSS input.
    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
        input.parse_comma_separated(KeyframePercentage::parse)
             .map(KeyframeSelector)
    }
}

/// A keyframe.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Keyframe {
    /// The selector this keyframe was specified from.
    pub selector: KeyframeSelector,

    /// The declaration block that was declared inside this keyframe.
    ///
    /// Note that `!important` rules in keyframes don't apply, but we keep this
    /// `Arc` just for convenience.
    #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
    pub block: Arc<RwLock<PropertyDeclarationBlock>>,
}

impl ToCss for Keyframe {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.selector.percentages().iter();
        try!(iter.next().unwrap().to_css(dest));
        for percentage in iter {
            try!(write!(dest, ", "));
            try!(percentage.to_css(dest));
        }
        try!(dest.write_str(" { "));
        try!(self.block.read().to_css(dest));
        try!(dest.write_str(" }"));
        Ok(())
    }
}

impl Keyframe {
    /// Parse a CSS keyframe.
    pub fn parse(css: &str,
                 parent_stylesheet: &Stylesheet,
                 extra_data: ParserContextExtraData)
                 -> Result<Arc<RwLock<Self>>, ()> {
        let error_reporter = Box::new(MemoryHoleReporter);
        let context = ParserContext::new_with_extra_data(parent_stylesheet.origin,
                                                         &parent_stylesheet.base_url,
                                                         error_reporter,
                                                         extra_data);
        let mut input = Parser::new(css);

        let mut rule_parser = KeyframeListParser {
            context: &context,
        };
        parse_one_rule(&mut input, &mut rule_parser)
    }
}

/// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of
/// declarations to apply.
///
/// TODO: Find a better name for this?
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
    /// A step formed by a declaration block specified by the CSS.
    Declarations {
        /// The declaration block per se.
        #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
        block: Arc<RwLock<PropertyDeclarationBlock>>
    },
    /// A synthetic step computed from the current computed values at the time
    /// of the animation.
    ComputedValues,
}

/// A single step from a keyframe animation.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesStep {
    /// The percentage of the animation duration when this step starts.
    pub start_percentage: KeyframePercentage,
    /// Declarations that will determine the final style during the step, or
    /// `ComputedValues` if this is an autogenerated step.
    pub value: KeyframesStepValue,
    /// Wether a animation-timing-function declaration exists in the list of
    /// declarations.
    ///
    /// This is used to know when to override the keyframe animation style.
    pub declared_timing_function: bool,
}

impl KeyframesStep {
    #[inline]
    fn new(percentage: KeyframePercentage,
           value: KeyframesStepValue) -> Self {
        let declared_timing_function = match value {
            KeyframesStepValue::Declarations { ref block } => {
                block.read().declarations.iter().any(|&(ref prop_decl, _)| {
                    match *prop_decl {
                        PropertyDeclaration::AnimationTimingFunction(..) => true,
                        _ => false,
                    }
                })
            }
            _ => false,
        };

        KeyframesStep {
            start_percentage: percentage,
            value: value,
            declared_timing_function: declared_timing_function,
        }
    }

    /// Return specified TransitionTimingFunction if this KeyframesSteps has 'animation-timing-function'.
    pub fn get_animation_timing_function(&self) -> Option<SpecifiedTimingFunction> {
        if !self.declared_timing_function {
            return None;
        }
        match self.value {
            KeyframesStepValue::Declarations { ref block } => {
                let guard = block.read();
                let &(ref declaration, _) =
                    guard.get(PropertyDeclarationId::Longhand(LonghandId::AnimationTimingFunction)).unwrap();
                match *declaration {
                    PropertyDeclaration::AnimationTimingFunction(ref value) => {
                        match *value {
                            DeclaredValue::Value(ref value) => {
                                // Use the first value.
                                Some(value.0[0])
                            },
                            _ => None,
                        }
                    },
                    _ => panic!(),
                }
            },
            KeyframesStepValue::ComputedValues => {
                panic!("Shouldn't happen to set animation-timing-function in missing keyframes")
            },
        }
    }
}

/// This structure represents a list of animation steps computed from the list
/// of keyframes, in order.
///
/// It only takes into account animable properties.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesAnimation {
    /// The difference steps of the animation.
    pub steps: Vec<KeyframesStep>,
    /// The properties that change in this animation.
    pub properties_changed: Vec<TransitionProperty>,
}

/// Get all the animated properties in a keyframes animation. Note that it's not
/// defined what happens when a property is not on a keyframe, so we only peek
/// the props of the first one.
///
/// In practice, browsers seem to try to do their best job at it, so we might
/// want to go through all the actual keyframes and deduplicate properties.
fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
    let mut ret = vec![];
    // NB: declarations are already deduplicated, so we don't have to check for
    // it here.
    for &(ref declaration, importance) in keyframe.block.read().declarations.iter() {
        assert!(!importance.important());

        if let Some(property) = TransitionProperty::from_declaration(declaration) {
            ret.push(property);
        }
    }

    ret
}

impl KeyframesAnimation {
    /// Create a keyframes animation from a given list of keyframes.
    ///
    /// This will return a keyframe animation with empty steps and
    /// properties_changed if the list of keyframes is empty, or there are no
    //  animated properties obtained from the keyframes.
    ///
    /// Otherwise, this will compute and sort the steps used for the animation,
    /// and return the animation object.
    pub fn from_keyframes(keyframes: &[Arc<RwLock<Keyframe>>]) -> Self {
        let mut result = KeyframesAnimation {
            steps: vec![],
            properties_changed: vec![],
        };

        if keyframes.is_empty() {
            return result;
        }

        result.properties_changed = get_animated_properties(&keyframes[0].read());
        if result.properties_changed.is_empty() {
            return result;
        }

        for keyframe in keyframes {
            let keyframe = keyframe.read();
            for percentage in keyframe.selector.0.iter() {
                result.steps.push(KeyframesStep::new(*percentage, KeyframesStepValue::Declarations {
                    block: keyframe.block.clone(),
                }));
            }
        }

        // Sort by the start percentage, so we can easily find a frame.
        result.steps.sort_by_key(|step| step.start_percentage);

        // Prepend autogenerated keyframes if appropriate.
        if result.steps[0].start_percentage.0 != 0. {
            result.steps.insert(0, KeyframesStep::new(KeyframePercentage::new(0.),
                                                      KeyframesStepValue::ComputedValues));
        }

        if result.steps.last().unwrap().start_percentage.0 != 1. {
            result.steps.push(KeyframesStep::new(KeyframePercentage::new(1.),
                                                 KeyframesStepValue::ComputedValues));
        }

        result
    }
}

/// Parses a keyframes list, like:
/// 0%, 50% {
///     width: 50%;
/// }
///
/// 40%, 60%, 100% {
///     width: 100%;
/// }
struct KeyframeListParser<'a> {
    context: &'a ParserContext<'a>,
}

/// Parses a keyframe list from CSS input.
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Arc<RwLock<Keyframe>>> {
    RuleListParser::new_for_nested_rule(input, KeyframeListParser { context: context })
        .filter_map(Result::ok)
        .collect()
}

enum Void {}
impl<'a> AtRuleParser for KeyframeListParser<'a> {
    type Prelude = Void;
    type AtRule = Arc<RwLock<Keyframe>>;
}

impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
    type Prelude = KeyframeSelector;
    type QualifiedRule = Arc<RwLock<Keyframe>>;

    fn parse_prelude(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> {
        let start = input.position();
        match KeyframeSelector::parse(input) {
            Ok(sel) => Ok(sel),
            Err(()) => {
                let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
                log_css_error(input, start, &message, self.context);
                Err(())
            }
        }
    }

    fn parse_block(&mut self, prelude: Self::Prelude, input: &mut Parser)
                   -> Result<Self::QualifiedRule, ()> {
        let parser = KeyframeDeclarationParser {
            context: self.context,
            declarations: vec![],
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            match declaration {
                Ok(_) => (),
                Err(range) => {
                    let pos = range.start;
                    let message = format!("Unsupported keyframe property declaration: '{}'",
                                          iter.input.slice(range));
                    log_css_error(iter.input, pos, &*message, self.context);
                }
            }
            // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
        }
        Ok(Arc::new(RwLock::new(Keyframe {
            selector: prelude,
            block: Arc::new(RwLock::new(PropertyDeclarationBlock {
                declarations: iter.parser.declarations,
                important_count: 0,
            })),
        })))
    }
}

struct KeyframeDeclarationParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    declarations: Vec<(PropertyDeclaration, Importance)>
}

/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for KeyframeDeclarationParser<'a, 'b> {
    type Prelude = ();
    type AtRule = ();
}

impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
    /// We parse rules directly into the declarations object
    type Declaration = ();

    fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
        let id = try!(PropertyId::parse(name.into()));
        let old_len = self.declarations.len();
        match PropertyDeclaration::parse(id, self.context, input, &mut self.declarations, true) {
            PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
            _ => {
                self.declarations.truncate(old_len);
                return Err(());
            }
        }
        // In case there is still unparsed text in the declaration, we should roll back.
        if !input.is_exhausted() {
            self.declarations.truncate(old_len);
            Err(())
        } else {
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>#[doc = "Rust TNetStrings serialization library."];

use std;
import option::{some, none};
import std::io;
import std::map;
import std::rand;

import io::writer_util;
import io::reader_util;

export t;
export str;
export int;
export float;
export bool;
export null;
export map;
export vec;
export to_writer;
export to_bytes;
export to_str;
export from_reader;
export from_bytes;
export from_str;

#[doc = "Represents a tnetstring value."]
enum t {
    #[doc = "str"]
    str([u8]),
    #[doc = "int"]
    int(int),
    #[doc = "floating"]
    float(float),
    #[doc = "boolean"]
    bool(bool),
    #[doc = "null"]
    null,
    #[doc = "map"]
    map(map::hashmap<[u8], t>),
    #[doc = "list"]
    vec([t]),
}

#[doc = "Serializes a tnetstring value into a io::writer."]
fn to_writer(writer: io::writer, t: t) {
    alt t {
        str(s) {
            writer.write_str(#fmt("%u:", vec::len(s)));
            writer.write(s);
            writer.write_char(',');
        }
        int(i) {
            let s = int::str(i);
            writer.write_str(#fmt("%u:%s#", str::len(s), s));
        }
        float(f) {
            let s = float::to_str(f, 6u);
            writer.write_str(#fmt("%u:%s^", str::len(s), s));
        }
        bool(b) {
            let s = bool::to_str(b);
            writer.write_str(#fmt("%u:%s!", str::len(s), s));
        }
        map(m) {
            let buf = io::mk_mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            m.items({ |key, value|
                to_writer(wr, str(key));
                to_writer(wr, value);
            });
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", vec::len(payload)));
            writer.write(payload);
            writer.write_char('}');
        }
        vec(l) {
            let buf = io::mk_mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            vec::iter(l, {|e| to_writer(wr, e) });
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", vec::len(payload)));
            writer.write(payload);
            writer.write_char(']');
        }
        null {
            writer.write_str("0:~");
        }
    }
}

#[doc = "Serializes a tnetstring value into a byte string."]
fn to_bytes(t: t) -> [u8] {
    let buf = io::mk_mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, t);
    io::mem_buffer_buf(buf)
}

#[doc = "Serializes a tnetstring value into a string."]
fn to_str(t: t) -> str {
    let buf = io::mk_mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, t);
    io::mem_buffer_str(buf)
}

#[doc = "Deserializes a tnetstring value from an io::reader."]
fn from_reader(reader: io::reader) -> option<t> {
    assert !reader.eof();

    let c = reader.read_byte();
    let len = 0u;

    // Note that netstring spec explicitly forbids padding zeros.
    // If the first char is zero, it must be the only char.
    if c < '0' as int || c > '9' as int {
        fail "Not a tnetstring: invalid or missing length prefix";
    } else if c == '0' as int {
        c = reader.read_byte();
    } else {
        do {
            len = (10u * len) + ((c as uint) - ('0' as uint));

            if reader.eof() {
                fail "Not a tnetstring: invalid or missing length prefix";
            }
            c = reader.read_byte();
        } while c >= '0' as int && c <= '9' as int;
    }

    // Validate end-of-length-prefix marker.
    if c != ':' as int {
        fail "Not a tnetstring: missing length prefix";
    }

    // Read the data plus terminating type tag.
    let payload = reader.read_bytes(len);

    if vec::len(payload) != len {
        fail "Not a tnetstring: invalid length prefix";
    }

    if reader.eof() {
        fail "Not a tnetstring: missing type tag";
    }

    alt reader.read_byte() as char {
      '#' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(int::from_str(s)) {|v| some(int(v)) }
      }
      '}' { some(map(parse_map(payload))) }
      ']' { some(vec(parse_vec(payload))) }
      '!' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(bool::from_str(s)) {|v| some(bool(v)) }
      }
      '^' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(float::from_str(s)) {|v| some(float(v)) }
      }
      '~' {
        assert vec::len(payload) == 0u;
        some(null)
      }
      ',' { some(str(payload)) }
      c {
        let s = str::from_char(c);
        fail #fmt("Invalid payload type: %?", s)
      }
    }
}

fn parse_vec(data: [u8]) -> [t] {
    if vec::len(data) == 0u { ret []; }

    let reader = io::bytes_reader(data);

    let value = from_reader(reader);
    assert option::is_some(value);
    let result = [option::get(value)];

    while !reader.eof() {
        let value = from_reader(reader);
        assert option::is_some(value);
        vec::push(result, option::get(value));
    }

    ret result;
}

fn parse_pair(reader: io::reader) -> ([u8], t) {
    let key = from_reader(reader);
    assert option::is_some(key);
    assert !reader.eof();

    let key = alt option::get(key) {
        str(key) { key }
        _ { fail "Keys can only be strings." }
    };

    let value = from_reader(reader);
    assert option::is_some(value);

    ret (key, option::get(value));
}

fn parse_map(data: [u8]) -> map::hashmap<[u8], t> {
    if vec::len(data) == 0u { ret map::new_bytes_hash(); }

    let reader = io::bytes_reader(data);
    let (key, value) = parse_pair(reader);

    let result = map::new_bytes_hash();
    result.insert(key, value);

    while !reader.eof() {
        let (key, value) = parse_pair(reader);
        result.insert(key, value);
    }

    ret result;
}

#[doc = "Deserializes a tnetstring value from a byte string."]
fn from_bytes(data: [u8]) -> (option<t>, [u8]) {
    let rdr = io::bytes_reader(data);
    let tnetstring = from_reader(rdr);
    (tnetstring, rdr.read_whole_stream())
}

#[doc = "Deserializes a tnetstring value from a string."]
fn from_str(data: str) -> (option<t>, str) {
    io::with_str_reader(data) {|rdr|
        let tnetstring = from_reader(rdr);
        let bytes = rdr.read_whole_stream();
        (tnetstring, str::from_bytes(bytes))
    }

}

#[doc = "Test the equality between two tnetstring values"]
fn eq(t0: t, t1: t) -> bool {
    alt (t0, t1) {
        (str(s0), str(s1)) { s0 == s1 }
        (int(i0), int(i1)) { i0 == i1 }
        (float(f0), float(f1)) { f0 == f1 }
        (bool(b0), bool(b1)) { b0 == b1 }
        (null, null) { true }
        (map(d0), map(d1)) {
            if d0.size() == d1.size() {
                let equal = true;
                d0.items() { |k, v|
                    if !d1.contains_key(k) || !eq(d1.get(k), v) {
                        equal = false;
                    }
                };
                equal
            } else {
                false
            }
        }
        (vec(l0), vec(l1)) {
            vec::all2(l0, l1, eq)
        }
        _ { false }
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: str, expected: t) {
        let (actual, rest) = from_str(string);
        assert option::is_some(actual);
        assert rest == "";

        let actual = option::get(actual);
        assert eq(actual, expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test("11:hello world,", str(str::bytes("hello world")));
        test("0:}", map(map::new_bytes_hash()));
        test("0:]", vec([]));

        let d = map::new_bytes_hash();
        d.insert(str::bytes("hello"),
                vec([
                    int(12345678901),
                    str(str::bytes("this")),
                    bool(true),
                    null,
                    str(str::bytes("\x00\x00\x00\x00"))]));

        test("51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00" +
            "\x00,]}", map(d));

        test("5:12345#", int(12345));
        test("12:this is cool,", str(str::bytes("this is cool")));
        test("0:,", str(str::bytes("")));
        test("0:~", null);
        test("4:true!", bool(true));
        test("5:false!", bool(false));
        test("10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str(str::bytes("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")));
        test("24:5:12345#5:67890#5:xxxxx,]",
            vec(
                [int(12345),
                int(67890),
                str(str::bytes("xxxxx"))]));
        test("18:3:0.1^3:0.2^3:0.4^]",
           vec([float(0.1), float(0.2), float(0.4)]));
        test("243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:" +
            "168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:" +
            "91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:" +
            "11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" +
            "]]]]",
            vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([
                    str(str::bytes("hello-there"))
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::rng, depth: u32) -> t {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(vec::init_fn(n as uint) { |_i|
                        get_random_object(rng, depth + 1u32)
                    })
                } else {
                    let d = map::new_bytes_hash();

                    let i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_bytes(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                alt randint(rng, 0u32, 5u32) {
                  0u32 { null }
                  1u32 { bool(true) }
                  2u32 { bool(false) }
                  3u32 {
                    if randint(rng, 0u32, 1u32) == 0u32 {
                        int(rng.next() as int)
                    } else {
                        int(-rng.next() as int)
                    }
                  }
                  4u32 {
                    let f = rng.next_float();

                    // Generate a float that can be exactly converted to
                    // and from a string.
                    while true {
                        alt float::from_str(float::to_str(f, 6u)) {
                          some(f1) {
                            if f == f1 { break; }
                            f = f1;
                          }
                          none { fail }
                        }
                    }

                    if randint(rng, 0u32, 1u32) == 0u32 {
                        float(f)
                    } else {
                        float(-f)
                    }
                  }
                  5u32 {
                    str(rng.gen_bytes(randint(rng, 0u32, 100u32) as uint))
                  }
                  _ { fail }
                }
            }
        }

        let rng = rand::mk_rng();

        let i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            alt from_bytes(to_bytes(v0)) {
                (some(v1), rest) if rest == [] {
                    assert eq(v0, v1);
                }
                _ { fail; }
            }
            i -= 1u;
        }
    }
}
<commit_msg>update to rust 7c70d35<commit_after>#[doc = "Rust TNetStrings serialization library."];

use std;
import option::{some, none};
import std::map;
import std::map::hashmap;
import std::rand;

import io::writer_util;
import io::reader_util;

export t;
export str;
export int;
export float;
export bool;
export null;
export map;
export vec;
export to_writer;
export to_bytes;
export to_str;
export from_reader;
export from_bytes;
export from_str;

#[doc = "Represents a tnetstring value."]
enum t {
    #[doc = "str"]
    str([u8]),
    #[doc = "int"]
    int(int),
    #[doc = "floating"]
    float(float),
    #[doc = "boolean"]
    bool(bool),
    #[doc = "null"]
    null,
    #[doc = "map"]
    map(map::hashmap<[u8], t>),
    #[doc = "list"]
    vec([t]),
}

#[doc = "Serializes a tnetstring value into a io::writer."]
fn to_writer(writer: io::writer, t: t) {
    alt t {
        str(s) {
            writer.write_str(#fmt("%u:", vec::len(s)));
            writer.write(s);
            writer.write_char(',');
        }
        int(i) {
            let s = int::str(i);
            writer.write_str(#fmt("%u:%s#", str::len(s), s));
        }
        float(f) {
            let s = float::to_str(f, 6u);
            writer.write_str(#fmt("%u:%s^", str::len(s), s));
        }
        bool(b) {
            let s = bool::to_str(b);
            writer.write_str(#fmt("%u:%s!", str::len(s), s));
        }
        map(m) {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            m.items({ |key, value|
                to_writer(wr, str(key));
                to_writer(wr, value);
            });
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", vec::len(payload)));
            writer.write(payload);
            writer.write_char('}');
        }
        vec(l) {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            vec::iter(l, {|e| to_writer(wr, e) });
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", vec::len(payload)));
            writer.write(payload);
            writer.write_char(']');
        }
        null {
            writer.write_str("0:~");
        }
    }
}

#[doc = "Serializes a tnetstring value into a byte string."]
fn to_bytes(t: t) -> [u8] {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, t);
    io::mem_buffer_buf(buf)
}

#[doc = "Serializes a tnetstring value into a string."]
fn to_str(t: t) -> str {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, t);
    io::mem_buffer_str(buf)
}

#[doc = "Deserializes a tnetstring value from an io::reader."]
fn from_reader(reader: io::reader) -> option<t> {
    assert !reader.eof();

    let c = reader.read_byte();
    let len = 0u;

    // Note that netstring spec explicitly forbids padding zeros.
    // If the first char is zero, it must be the only char.
    if c < '0' as int || c > '9' as int {
        fail "Not a tnetstring: invalid or missing length prefix";
    } else if c == '0' as int {
        c = reader.read_byte();
    } else {
        do {
            len = (10u * len) + ((c as uint) - ('0' as uint));

            if reader.eof() {
                fail "Not a tnetstring: invalid or missing length prefix";
            }
            c = reader.read_byte();
        } while c >= '0' as int && c <= '9' as int;
    }

    // Validate end-of-length-prefix marker.
    if c != ':' as int {
        fail "Not a tnetstring: missing length prefix";
    }

    // Read the data plus terminating type tag.
    let payload = reader.read_bytes(len);

    if vec::len(payload) != len {
        fail "Not a tnetstring: invalid length prefix";
    }

    if reader.eof() {
        fail "Not a tnetstring: missing type tag";
    }

    alt reader.read_byte() as char {
      '#' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(int::from_str(s)) {|v| some(int(v)) }
      }
      '}' { some(map(parse_map(payload))) }
      ']' { some(vec(parse_vec(payload))) }
      '!' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(bool::from_str(s)) {|v| some(bool(v)) }
      }
      '^' {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        option::chain(float::from_str(s)) {|v| some(float(v)) }
      }
      '~' {
        assert vec::len(payload) == 0u;
        some(null)
      }
      ',' { some(str(payload)) }
      c {
        let s = str::from_char(c);
        fail #fmt("Invalid payload type: %?", s)
      }
    }
}

fn parse_vec(data: [u8]) -> [t] {
    if vec::len(data) == 0u { ret []; }

    let reader = io::bytes_reader(data);

    let value = from_reader(reader);
    assert option::is_some(value);
    let result = [option::get(value)];

    while !reader.eof() {
        let value = from_reader(reader);
        assert option::is_some(value);
        vec::push(result, option::get(value));
    }

    ret result;
}

fn parse_pair(reader: io::reader) -> ([u8], t) {
    let key = from_reader(reader);
    assert option::is_some(key);
    assert !reader.eof();

    let key = alt option::get(key) {
        str(key) { key }
        _ { fail "Keys can only be strings." }
    };

    let value = from_reader(reader);
    assert option::is_some(value);

    ret (key, option::get(value));
}

fn parse_map(data: [u8]) -> map::hashmap<[u8], t> {
    if vec::len(data) == 0u { ret map::new_bytes_hash(); }

    let reader = io::bytes_reader(data);
    let (key, value) = parse_pair(reader);

    let result = map::new_bytes_hash();
    result.insert(key, value);

    while !reader.eof() {
        let (key, value) = parse_pair(reader);
        result.insert(key, value);
    }

    ret result;
}

#[doc = "Deserializes a tnetstring value from a byte string."]
fn from_bytes(data: [u8]) -> (option<t>, [u8]) {
    let rdr = io::bytes_reader(data);
    let tnetstring = from_reader(rdr);
    (tnetstring, rdr.read_whole_stream())
}

#[doc = "Deserializes a tnetstring value from a string."]
fn from_str(data: str) -> (option<t>, str) {
    io::with_str_reader(data) {|rdr|
        let tnetstring = from_reader(rdr);
        let bytes = rdr.read_whole_stream();
        (tnetstring, str::from_bytes(bytes))
    }

}

#[doc = "Test the equality between two tnetstring values"]
fn eq(t0: t, t1: t) -> bool {
    alt (t0, t1) {
        (str(s0), str(s1)) { s0 == s1 }
        (int(i0), int(i1)) { i0 == i1 }
        (float(f0), float(f1)) { f0 == f1 }
        (bool(b0), bool(b1)) { b0 == b1 }
        (null, null) { true }
        (map(d0), map(d1)) {
            if d0.size() == d1.size() {
                let equal = true;
                d0.items() { |k, v|
                    if !d1.contains_key(k) || !eq(d1.get(k), v) {
                        equal = false;
                    }
                };
                equal
            } else {
                false
            }
        }
        (vec(l0), vec(l1)) {
            vec::all2(l0, l1, eq)
        }
        _ { false }
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: str, expected: t) {
        let (actual, rest) = from_str(string);
        assert option::is_some(actual);
        assert rest == "";

        let actual = option::get(actual);
        assert eq(actual, expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test("11:hello world,", str(str::bytes("hello world")));
        test("0:}", map(map::new_bytes_hash()));
        test("0:]", vec([]));

        let d = map::new_bytes_hash();
        d.insert(str::bytes("hello"),
                vec([
                    int(12345678901),
                    str(str::bytes("this")),
                    bool(true),
                    null,
                    str(str::bytes("\x00\x00\x00\x00"))]));

        test("51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00" +
            "\x00,]}", map(d));

        test("5:12345#", int(12345));
        test("12:this is cool,", str(str::bytes("this is cool")));
        test("0:,", str(str::bytes("")));
        test("0:~", null);
        test("4:true!", bool(true));
        test("5:false!", bool(false));
        test("10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str(str::bytes("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")));
        test("24:5:12345#5:67890#5:xxxxx,]",
            vec(
                [int(12345),
                int(67890),
                str(str::bytes("xxxxx"))]));
        test("18:3:0.1^3:0.2^3:0.4^]",
           vec([float(0.1), float(0.2), float(0.4)]));
        test("243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:" +
            "168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:" +
            "91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:" +
            "11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" +
            "]]]]",
            vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([vec([vec([vec([vec([vec([vec(
                [vec([vec([
                    str(str::bytes("hello-there"))
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::rng, depth: u32) -> t {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(vec::from_fn(n as uint) { |_i|
                        get_random_object(rng, depth + 1u32)
                    })
                } else {
                    let d = map::new_bytes_hash();

                    let i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_bytes(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                alt randint(rng, 0u32, 5u32) {
                  0u32 { null }
                  1u32 { bool(true) }
                  2u32 { bool(false) }
                  3u32 {
                    if randint(rng, 0u32, 1u32) == 0u32 {
                        int(rng.next() as int)
                    } else {
                        int(-rng.next() as int)
                    }
                  }
                  4u32 {
                    let f = rng.next_float();

                    // Generate a float that can be exactly converted to
                    // and from a string.
                    while true {
                        alt float::from_str(float::to_str(f, 6u)) {
                          some(f1) {
                            if f == f1 { break; }
                            f = f1;
                          }
                          none { fail }
                        }
                    }

                    if randint(rng, 0u32, 1u32) == 0u32 {
                        float(f)
                    } else {
                        float(-f)
                    }
                  }
                  5u32 {
                    str(rng.gen_bytes(randint(rng, 0u32, 100u32) as uint))
                  }
                  _ { fail }
                }
            }
        }

        let rng = rand::rng();

        let i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            alt from_bytes(to_bytes(v0)) {
                (some(v1), rest) if rest == [] {
                    assert eq(v0, v1);
                }
                _ { fail; }
            }
            i -= 1u;
        }
    }
}
<|endoftext|>