<commit_before>use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => println!("{}", why),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => println!("{}", why),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    if code == "ENDB" && len == 0 {
                        break;
                    } else if code == "DNA1" {
                        // DNA_sdna_from_data
                        let mut counter = 0u;
                        // SDNA
                        let mut char4 = [0u8, ..4];
                        match file.read(char4) {
                            Err(why) => println!("{}", why),
                            Ok(_) => (),
                        }
                        counter += 4;
                        let mut str4 = String::new();
                        for n in range(0u, 4) {
                            str4.push(char4[n] as char);
                        }
                        let slice = str4.as_slice();
                        let code: &str = slice.slice(0, 4);
                        println!("  code = {}", code);
                        if code == "SDNA" {
                            // NAME
                            let mut char4 = [0u8, ..4];
                            match file.read(char4) {
                                Err(why) => println!("{}", why),
                                Ok(_) => (),
                            }
                            counter += 4;
                            let mut str4 = String::new();
                            for n in range(0u, 4) {
                                str4.push(char4[n] as char);
                            }
                            let slice = str4.as_slice();
                            let code: &str = slice.slice(0, 4);
                            println!("  code = {}", code);
                            if code == "NAME" {
                                // nr_names
                                let io_result = file.read_le_u32();
                                let nr_names: u32 = io_result.unwrap();
                                counter += 4;
                                println!("  nr_names = {}", nr_names);
                                let mut nr = 0u32;
                                loop {
                                    let mut name = String::new();
                                    loop {
                                        // expect strings with '\0' as terminator
                                        let io_result = file.read_byte();
                                        let byte: u8 = io_result.unwrap();
                                        counter += 1;
                                        if byte != 0 {
                                            name.push(byte as char);
                                        } else {
                                            println!("    name = '{}'", name);
                                            nr += 1;
                                            break;
                                        }
                                    } // name loop
                                    if nr >= nr_names {
                                        break;
                                    }
                                } // nr_names loop
                                // assume fill bytes with '\0'
                                let mut last_byte;
                                loop {
                                    let io_result = file.read_byte();
                                    let byte: u8 = io_result.unwrap();
                                    counter += 1;
                                    if byte != 0 {
                                        last_byte = byte;
                                        break;
                                    }
                                } // fill bytes
                                // TYPE
                                let mut char3 = [0u8, ..3];
                                match file.read(char3) {
                                    Err(why) => println!("{}", why),
                                    Ok(_) => (),
                                }
                                counter += 3;
                                let mut str4 = String::new();
                                str4.push(last_byte as char);
                                for n in range(0u, 3) {
                                    str4.push(char3[n] as char);
                                }
                                let slice = str4.as_slice();
                                let code: &str = slice.slice(0, 4);
                                println!("  code = {}", code);
                                if code == "TYPE" {
                                    // nr_types
                                    let io_result = file.read_le_u32();
                                    let nr_types: u32 = io_result.unwrap();
                                    counter += 4;
                                    println!("  nr_types = {}", nr_types);
                                    let mut nr = 0u32;
                                    loop {
                                        let mut name = String::new();
                                        loop {
                                            // expect strings with
                                            // '\0' as terminator
                                            let io_result = file.read_byte();
                                            let byte: u8 = io_result.unwrap();
                                            counter += 1;
                                            if byte != 0 {
                                                name.push(byte as char);
                                            } else {
                                                println!("    type = '{}'",
                                                         name);
                                                nr += 1;
                                                break;
                                            }
                                        } // type loop
                                        if nr >= nr_types {
                                            break;
                                        }
                                    } // nr_types loop
                                    // assume fill bytes with '\0'
                                    let mut last_byte;
                                    loop {
                                        let io_result = file.read_byte();
                                        let byte: u8 = io_result.unwrap();
                                        counter += 1;
                                        if byte != 0 {
                                            last_byte = byte;
                                            break;
                                        }
                                    } // fill bytes
                                    // TLEN
                                    let mut char3 = [0u8, ..3];
                                    match file.read(char3) {
                                        Err(why) => println!("{}", why),
                                        Ok(_) => (),
                                    }
                                    counter += 3;
                                    let mut str4 = String::new();
                                    str4.push(last_byte as char);
                                    for n in range(0u, 3) {
                                        str4.push(char3[n] as char);
                                    }
                                    let slice = str4.as_slice();
                                    let code: &str = slice.slice(0, 4);
                                    println!("  code = {}", code);
                                    if code == "TLEN" {
                                        // typelens
                                        let io_result = file.read_le_u16();
                                        let typelens: u16 = io_result.unwrap();
                                        counter += 2;
                                        println!("    typelens = {}", typelens);
                                        // skip nr_types times u16 values
                                        let _dummy = file.read_exact(2u *
                                                                     nr_types as
                                                                     uint);
                                        counter += 2u * nr_types as uint;
                                        // check next byte
                                        let io_result = file.read_byte();
                                        last_byte = io_result.unwrap();
                                        counter += 1;
                                        if last_byte == 0 {
                                            let io_result = file.read_byte();
                                            last_byte = io_result.unwrap();
                                            counter += 1;
                                        }
                                        // STRC
                                        let mut char3 = [0u8, ..3];
                                        match file.read(char3) {
                                            Err(why) => println!("{}", why),
                                            Ok(_) => (),
                                        }
                                        counter += 3;
                                        let mut str4 = String::new();
                                        str4.push(last_byte as char);
                                        for n in range(0u, 3) {
                                            str4.push(char3[n] as char);
                                        }
                                        let slice = str4.as_slice();
                                        let code: &str = slice.slice(0, 4);
                                        println!("  code = {}", code);
                                        if code == "STRC" {
                                        }
                                    }
                                }
                            }
                        }
                        // read remaining stuff
                        let _dummy = file.read_exact(len - counter);
                    } else {
                        let _dummy = file.read_exact(len);
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<commit_msg>We read the Blender DNA correctly, but don't store anything (yet).<commit_after>use std::io::File;
use std::mem;
use std::os;

// https://stackoverflow.com/questions/24633784/is-there-a-gzip-library-available-for-rust

fn pointer_size() -> uint {
    let tmp = 0u8;
    let boxed = box tmp;
    mem::size_of_val(&boxed)
}

fn main() {
    let args = os::args();
    if args.len() != 2 {
        println!("usage: read_blend_file /path/to/file.blend");
    } else {
        let slice: &str = args[1].as_slice();
        println!("open \"{}\" ...", slice);
        {
            // read 12 bytes from the Blender file
            let path = Path::new(slice);
            let display = path.display();
            let mut file = match File::open(&path) {
                Err(why) => fail!("couldn't open {}: {}", display, why.desc),
                Ok(file) => file,
            };
            let mut buf = [0u8, ..12];
            match file.read(buf) {
                Err(why) => println!("{}", why),
                Ok(_) => (),
            }
            // pack those 12 bytes into a string ...
            let mut header = String::new();
            for n in range(0u, 12) {
                header.push(buf[n] as char);
            }
            // ... to be able to compare them
            let slice = header.as_slice();
            let blender: &str = slice.slice(0, 7); // first 7 chars
            if blender == "BLENDER" {
                println!("INFO: a Blender file.");
                let ptr_size = pointer_size();
                println!("size of pointer in bytes: {}", ptr_size);
                println!("Header (12 chars): {}", header);
                let mut ptr_size_differs = false;
                if buf[7] as char == '_' {
                    // 32-bit pointers expected
                    if ptr_size != 4 {
                        ptr_size_differs = true;
                    }
                    println!("32-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                } else {
                    // 64-bit pointers expected
                    if ptr_size != 8 {
                        ptr_size_differs = true;
                    }
                    println!("64-bit pointers in file, pointer size differs? {}", ptr_size_differs);
                }
                // little endian or big endian?
                let mut l_endian;
                let endian_test = 1u16; // value one stored in two bytes
                if endian_test.to_le() != endian_test {
                    l_endian = false;
                } else {
                    // if we are on a little endian machine to_le() is a no-op
                    l_endian = true;
                }
                println!("l_endian = {}", l_endian);
                let mut switch_endian;
                if buf[8] as char == 'v' {
                    // file stores little endian
                    if !l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                } else {
                    // file stores big endian
                    if l_endian {
                        switch_endian = true;
                    } else {
                        switch_endian = false;
                    }
                }
                println!("switch_endian = {}", switch_endian);
                // get the version number
                let mut num = String::new();
                for n in range(9u, 12) {
                    num.push(buf[n] as char);
                }
                println!("num = {}", num);
                // assumes 64-bit pointers (in file as well as on platform)
                loop {
                    // read_file_dna
                    let mut bhead8 = [0u8, ..24]; // 4 * int + 64-bit pointer
                    match file.read(bhead8) {
                        Err(why) => println!("{}", why),
                        Ok(_) => (),
                    }
                    // pack those 24 bytes into a string ...
                    let mut bhead = String::new();
                    for n in range(0u, 24) {
                        bhead.push(bhead8[n] as char);
                    }
                    let slice = bhead.as_slice();
                    let code: &str = slice.slice(0, 4); // first 4 chars
                    println!("code = {}", code);
                    let mut len: uint = 0;
                    len += (bhead8[4] as uint) <<  0;
                    len += (bhead8[5] as uint) <<  8;
                    len += (bhead8[6] as uint) << 16;
                    len += (bhead8[7] as uint) << 24;
                    println!("len = {}", len);
                    if code == "ENDB" && len == 0 {
                        break;
                    } else if code == "DNA1" {
                        // DNA_sdna_from_data
                        let mut counter = 0u;
                        // SDNA
                        let mut char4 = [0u8, ..4];
                        match file.read(char4) {
                            Err(why) => println!("{}", why),
                            Ok(_) => (),
                        }
                        counter += 4;
                        let mut str4 = String::new();
                        for n in range(0u, 4) {
                            str4.push(char4[n] as char);
                        }
                        let slice = str4.as_slice();
                        let code: &str = slice.slice(0, 4);
                        println!("  code = {}", code);
                        if code == "SDNA" {
                            // NAME
                            let mut char4 = [0u8, ..4];
                            match file.read(char4) {
                                Err(why) => println!("{}", why),
                                Ok(_) => (),
                            }
                            counter += 4;
                            let mut str4 = String::new();
                            for n in range(0u, 4) {
                                str4.push(char4[n] as char);
                            }
                            let slice = str4.as_slice();
                            let code: &str = slice.slice(0, 4);
                            println!("  code = {}", code);
                            if code == "NAME" {
                                // nr_names
                                let io_result = file.read_le_u32();
                                let nr_names: u32 = io_result.unwrap();
                                counter += 4;
                                println!("  nr_names = {}", nr_names);
                                let mut nr = 0u32;
                                loop {
                                    let mut name = String::new();
                                    loop {
                                        // expect strings with '\0' as terminator
                                        let io_result = file.read_byte();
                                        let byte: u8 = io_result.unwrap();
                                        counter += 1;
                                        if byte != 0 {
                                            name.push(byte as char);
                                        } else {
                                            println!("    name = '{}'", name);
                                            nr += 1;
                                            break;
                                        }
                                    } // name loop
                                    if nr >= nr_names {
                                        break;
                                    }
                                } // nr_names loop
                                // assume fill bytes with '\0'
                                let mut last_byte;
                                loop {
                                    let io_result = file.read_byte();
                                    let byte: u8 = io_result.unwrap();
                                    counter += 1;
                                    if byte != 0 {
                                        last_byte = byte;
                                        break;
                                    }
                                } // fill bytes
                                // TYPE
                                let mut char3 = [0u8, ..3];
                                match file.read(char3) {
                                    Err(why) => println!("{}", why),
                                    Ok(_) => (),
                                }
                                counter += 3;
                                let mut str4 = String::new();
                                str4.push(last_byte as char);
                                for n in range(0u, 3) {
                                    str4.push(char3[n] as char);
                                }
                                let slice = str4.as_slice();
                                let code: &str = slice.slice(0, 4);
                                println!("  code = {}", code);
                                if code == "TYPE" {
                                    // nr_types
                                    let io_result = file.read_le_u32();
                                    let nr_types: u32 = io_result.unwrap();
                                    counter += 4;
                                    println!("  nr_types = {}", nr_types);
                                    let mut nr = 0u32;
                                    loop {
                                        let mut name = String::new();
                                        loop {
                                            // expect strings with
                                            // '\0' as terminator
                                            let io_result = file.read_byte();
                                            let byte: u8 = io_result.unwrap();
                                            counter += 1;
                                            if byte != 0 {
                                                name.push(byte as char);
                                            } else {
                                                println!("    type = '{}'",
                                                         name);
                                                nr += 1;
                                                break;
                                            }
                                        } // type loop
                                        if nr >= nr_types {
                                            break;
                                        }
                                    } // nr_types loop
                                    // assume fill bytes with '\0'
                                    let mut last_byte;
                                    loop {
                                        let io_result = file.read_byte();
                                        let byte: u8 = io_result.unwrap();
                                        counter += 1;
                                        if byte != 0 {
                                            last_byte = byte;
                                            break;
                                        }
                                    } // fill bytes
                                    // TLEN
                                    let mut char3 = [0u8, ..3];
                                    match file.read(char3) {
                                        Err(why) => println!("{}", why),
                                        Ok(_) => (),
                                    }
                                    counter += 3;
                                    let mut str4 = String::new();
                                    str4.push(last_byte as char);
                                    for n in range(0u, 3) {
                                        str4.push(char3[n] as char);
                                    }
                                    let slice = str4.as_slice();
                                    let code: &str = slice.slice(0, 4);
                                    println!("  code = {}", code);
                                    if code == "TLEN" {
                                        // typelens
                                        let io_result = file.read_le_u16();
                                        let typelens: u16 = io_result.unwrap();
                                        counter += 2;
                                        println!("    typelens = {}", typelens);
                                        // skip nr_types times u16 values
                                        let _dummy = file.read_exact(2u *
                                                                     nr_types as
                                                                     uint);
                                        counter += 2u * nr_types as uint;
                                        // check next byte
                                        let io_result = file.read_byte();
                                        last_byte = io_result.unwrap();
                                        counter += 1;
                                        if last_byte == 0 {
                                            let io_result = file.read_byte();
                                            last_byte = io_result.unwrap();
                                            counter += 1;
                                        }
                                        // STRC
                                        let mut char3 = [0u8, ..3];
                                        match file.read(char3) {
                                            Err(why) => println!("{}", why),
                                            Ok(_) => (),
                                        }
                                        counter += 3;
                                        let mut str4 = String::new();
                                        str4.push(last_byte as char);
                                        for n in range(0u, 3) {
                                            str4.push(char3[n] as char);
                                        }
                                        let slice = str4.as_slice();
                                        let code: &str = slice.slice(0, 4);
                                        println!("  code = {}", code);
                                        if code == "STRC" {
                                            // nr_structs
                                            let io_result = file.read_le_u32();
                                            let nr_structs: u32 = io_result.unwrap();
                                            counter += 4;
                                            println!("    nr_structs = {}", nr_structs);
                                            nr = 0u32;
                                            loop {
                                                // sp0
                                                let io_result = file.read_le_u16();
                                                let _sp0: u16 = io_result.unwrap();
                                                counter += 2;
                                                // sp1
                                                let io_result = file.read_le_u16();
                                                let sp1: u16 = io_result.unwrap();
                                                counter += 2;
                                                for _n in range(0u16, 2 * sp1) {
                                                    let io_result = file.read_le_u16();
                                                    let _u: u16 = io_result.unwrap();
                                                    counter += 2;
                                                }
                                                nr += 1;
                                                if nr >= nr_structs {
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        // read remaining stuff
                        println!("skip {} bytes", len - counter)
                        let _dummy = file.read_exact(len - counter);
                    } else {
                        let _dummy = file.read_exact(len);
                    }
                }
            } else {
                println!("ERROR: not a Blender file.");
            }
        } // file gets closed here
    }
}
<|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/. */

//! Common handling for the specified value CSS url() values.

use cssparser::Parser;
use gecko_bindings::bindings;
use gecko_bindings::structs::root::nsStyleImageRequest;
use gecko_bindings::structs::root::mozilla::CORSMode;
use gecko_bindings::structs::root::mozilla::css::URLValue;
use gecko_bindings::sugar::ownership::{HasArcFFI, FFIArcHelpers};
use gecko_bindings::sugar::refptr::RefPtr;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
use stylesheets::UrlExtraData;
use values::computed::{Context, ToComputedValue};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    #[css(skip)]
    pub url_value: RefPtr<URLValue>,
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = unsafe {
            let ptr = bindings::Gecko_URLValue_Create(
                url.0.clone().into_strong(),
                cors,
            );
            // We do not expect Gecko_URLValue_Create returns null.
            debug_assert!(!ptr.is_null());
            RefPtr::from_addrefed(ptr)
        };
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString) -> (),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &self.0.url_value,
            dest,
            bindings::Gecko_GetComputedURLSpec,
        )
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl { url, url_value })
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value.get()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &(self.0).0.url_value,
            dest,
            bindings::Gecko_GetComputedImageURLSpec,
        )
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value }))
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value.get()
    }
}
<commit_msg>style: Make css::URLValue::IsLocalRef call into CssUrlData::is_fragment.<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/. */

//! Common handling for the specified value CSS url() values.

use cssparser::Parser;
use gecko_bindings::bindings;
use gecko_bindings::structs::root::nsStyleImageRequest;
use gecko_bindings::structs::root::mozilla::CORSMode;
use gecko_bindings::structs::root::mozilla::css::URLValue;
use gecko_bindings::sugar::ownership::{HasArcFFI, FFIArcHelpers};
use gecko_bindings::sugar::refptr::RefPtr;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
use stylesheets::UrlExtraData;
use values::computed::{Context, ToComputedValue};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    #[inline]
    pub fn is_fragment(&self) -> bool {
        self.0.is_fragment()
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    #[css(skip)]
    pub url_value: RefPtr<URLValue>,
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = unsafe {
            let ptr = bindings::Gecko_URLValue_Create(
                url.0.clone().into_strong(),
                cors,
            );
            // We do not expect Gecko_URLValue_Create returns null.
            debug_assert!(!ptr.is_null());
            RefPtr::from_addrefed(ptr)
        };
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString) -> (),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &self.0.url_value,
            dest,
            bindings::Gecko_GetComputedURLSpec,
        )
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl { url, url_value })
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value.get()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &(self.0).0.url_value,
            dest,
            bindings::Gecko_GetComputedImageURLSpec,
        )
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value }))
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value.get()
    }
}
<|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/. */

use darling::{FromDeriveInput, FromField, FromVariant};
use quote::{ToTokens, Tokens};
use std::borrow::Cow;
use std::collections::HashSet;
use std::iter;
use syn::{self, AngleBracketedParameterData, Body, DeriveInput, Field, Ident};
use syn::{ImplGenerics, Path, PathParameters, PathSegment, PolyTraitRef};
use syn::{QSelf, TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound};
use syn::{TypeBinding, Variant, WhereBoundPredicate, WherePredicate};
use syn::visit::{self, Visitor};
use synstructure::{self, BindOpts, BindStyle, BindingInfo};

pub struct WhereClause<'input, 'path> {
    pub inner: syn::WhereClause,
    pub params: &'input [TyParam],
    trait_path: &'path [&'path str],
    trait_output: Option<&'path str>,
    bounded_types: HashSet<Ty>,
}

impl<'input, 'path> ToTokens for WhereClause<'input, 'path> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        self.inner.to_tokens(tokens);
    }
}

impl<'input, 'path> WhereClause<'input, 'path> {
    pub fn add_trait_bound(&mut self, ty: &Ty) {
        let trait_path = self.trait_path;
        let params = self.params;
        let mut found = self.trait_output.map(|_| HashSet::new());
        if self.bounded_types.contains(&ty) {
            return;
        }
        if !is_parameterized(&ty, params, found.as_mut()) {
            return;
        }
        self.bounded_types.insert(ty.clone());

        let output = if let Some(output) = self.trait_output {
            output
        } else {
            self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
            return;
        };

        if let Ty::Path(None, ref path) = *ty {
            if path_to_ident(path).is_some() {
                self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
                return;
            }
        }

        let output_type = map_type_params(ty, params, &mut |ident| {
            let ty = Ty::Path(None, ident.clone().into());
            fmap_output_type(ty, trait_path, output)
        });

        let pred = where_predicate(
            ty.clone(),
            trait_path,
            Some((output, output_type)),
        );

        self.inner.predicates.push(pred);

        if let Some(found) = found {
            for ident in found {
                let ty = Ty::Path(None, ident.into());
                if !self.bounded_types.contains(&ty) {
                    self.bounded_types.insert(ty.clone());
                    self.inner.predicates.push(
                        where_predicate(ty, trait_path, None),
                    );
                };
            }
        }
    }
}

pub fn fmap_match<F>(
    input: &DeriveInput,
    bind_style: BindStyle,
    mut f: F,
) -> Tokens
where
    F: FnMut(BindingInfo) -> Tokens,
{
    synstructure::each_variant(input, &bind_style.into(), |fields, variant| {
        let name = variant_ctor(input, variant);
        let (mapped, mapped_fields) = value(&name, variant, "mapped");
        let fields_pairs = fields.into_iter().zip(mapped_fields);
        let mut computations = quote!();
        computations.append_all(fields_pairs.map(|(field, mapped_field)| {
            let expr = f(field);
            quote! { let #mapped_field = #expr; }
        }));
        computations.append(mapped);
        Some(computations)
    })
}

fn fmap_output_type(
    ty: Ty,
    trait_path: &[&str],
    trait_output: &str,
) -> Ty {
    Ty::Path(
        Some(QSelf {
            ty: Box::new(ty),
            position: trait_path.len(),
        }),
        path(trait_path.iter().chain(iter::once(&trait_output))),
    )
}

pub fn fmap_trait_parts<'input, 'path>(
    input: &'input DeriveInput,
    trait_path: &'path [&'path str],
    trait_output: &'path str,
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>, Path) {
    let (impl_generics, ty_generics, mut where_clause) = trait_parts(input, trait_path);
    where_clause.trait_output = Some(trait_output);
    let output_ty = PathSegment {
        ident: input.ident.clone(),
        parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
            lifetimes: input.generics.lifetimes.iter().map(|l| l.lifetime.clone()).collect(),
            types: input.generics.ty_params.iter().map(|ty| {
                fmap_output_type(
                    Ty::Path(None, ty.ident.clone().into()),
                    trait_path,
                    trait_output,
                )
            }).collect(),
            .. Default::default()
        }),
    }.into();
    (impl_generics, ty_generics, where_clause, output_ty)
}

pub fn is_parameterized(
    ty: &Ty,
    params: &[TyParam],
    found: Option<&mut HashSet<Ident>>,
) -> bool {
    struct IsParameterized<'a, 'b> {
        params: &'a [TyParam],
        has_free: bool,
        found: Option<&'b mut HashSet<Ident>>,
    }

    impl<'a, 'b> Visitor for IsParameterized<'a, 'b> {
        fn visit_path(&mut self, path: &Path) {
            if let Some(ident) = path_to_ident(path) {
                if self.params.iter().any(|param| param.ident == ident) {
                    self.has_free = true;
                    if let Some(ref mut found) = self.found {
                        found.insert(ident.clone());
                    }
                }
            }
            visit::walk_path(self, path);
        }
    }

    let mut visitor = IsParameterized { params, has_free: false, found };
    visitor.visit_ty(ty);
    visitor.has_free
}

pub fn map_type_params<F>(ty: &Ty, params: &[TyParam], f: &mut F) -> Ty
where
    F: FnMut(&Ident) -> Ty,
{
    match *ty {
        Ty::Slice(ref ty) => Ty::Slice(Box::new(map_type_params(ty, params, f))),
        Ty::Array(ref ty, ref expr) => {
            Ty::Array(Box::new(map_type_params(ty, params, f)), expr.clone())
        },
        Ty::Never => Ty::Never,
        Ty::Tup(ref items) => {
            Ty::Tup(items.iter().map(|ty| map_type_params(ty, params, f)).collect())
        },
        Ty::Path(None, ref path) => {
            if let Some(ident) = path_to_ident(path) {
                if params.iter().any(|param| param.ident == ident) {
                    return f(ident);
                }
            }
            Ty::Path(None, map_type_params_in_path(path, params, f))
        }
        Ty::Path(ref qself, ref path) => {
            Ty::Path(
                qself.as_ref().map(|qself| {
                    QSelf {
                        ty: Box::new(map_type_params(&qself.ty, params, f)),
                        position: qself.position,
                    }
                }),
                map_type_params_in_path(path, params, f),
            )
        },
        Ty::Paren(ref ty) => Ty::Paren(Box::new(map_type_params(ty, params, f))),
        ref ty => panic!("type {:?} cannot be mapped yet", ty),
    }
}

fn map_type_params_in_path<F>(path: &Path, params: &[TyParam], f: &mut F) -> Path
where
    F: FnMut(&Ident) -> Ty,
{
    Path {
        global: path.global,
        segments: path.segments.iter().map(|segment| {
            PathSegment {
                ident: segment.ident.clone(),
                parameters: match segment.parameters {
                    PathParameters::AngleBracketed(ref data) => {
                        PathParameters::AngleBracketed(AngleBracketedParameterData {
                            lifetimes: data.lifetimes.clone(),
                            types: data.types.iter().map(|ty| {
                                map_type_params(ty, params, f)
                            }).collect(),
                            bindings: data.bindings.iter().map(|binding| {
                                TypeBinding {
                                    ident: binding.ident.clone(),
                                    ty: map_type_params(&binding.ty, params, f),
                                }
                            }).collect(),
                        })
                    },
                    ref parameters => {
                        panic!("parameters {:?} cannot be mapped yet", parameters)
                    },
                },
            }
        }).collect(),
    }
}

pub fn path<S>(segments: S) -> Path
where
    S: IntoIterator,
    <S as IntoIterator>::Item: AsRef<str>,
{
    Path {
        global: true,
        segments: segments.into_iter().map(|s| s.as_ref().into()).collect(),
    }
}

fn path_to_ident(path: &Path) -> Option<&Ident> {
    match *path {
        Path { global: false, ref segments } if segments.len() == 1 => {
            if segments[0].parameters.is_empty() {
                Some(&segments[0].ident)
            } else {
                None
            }
        },
        _ => None,
    }
}

pub fn parse_field_attrs<A>(field: &Field) -> A
where
    A: FromField,
{
    match A::from_field(field) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse field attributes: {}", e),
    }
}

pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
where
    A: FromDeriveInput,
{
    match A::from_derive_input(input) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse input attributes: {}", e),
    }
}

pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
    A: FromVariant,
{
    match A::from_variant(variant) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse variant attributes: {}", e),
    }
}

pub fn ref_pattern<'a>(
    name: &Ident,
    variant: &'a Variant,
    prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
    synstructure::match_pattern(
        &name,
        &variant.data,
        &BindOpts::with_prefix(BindStyle::Ref, prefix.to_owned()),
    )
}

pub fn trait_parts<'input, 'path>(
    input: &'input DeriveInput,
    trait_path: &'path [&'path str],
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>) {
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let where_clause = WhereClause {
        inner: where_clause.clone(),
        params: &input.generics.ty_params,
        trait_path,
        trait_output: None,
        bounded_types: HashSet::new()
    };
    (impl_generics, ty_generics, where_clause)
}

fn trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path {
    let (name, parent) = path.split_last().unwrap();
    let last_segment = PathSegment {
        ident: (*name).into(),
        parameters: PathParameters::AngleBracketed(
            AngleBracketedParameterData {
                bindings: output.into_iter().map(|(param, ty)| {
                    TypeBinding { ident: param.into(), ty }
                }).collect(),
                .. Default::default()
            }
        )
    };
    Path {
        global: true,
        segments: {
            parent
                .iter()
                .cloned()
                .map(Into::into)
                .chain(iter::once(last_segment))
                .collect()
        },
    }
}

pub fn value<'a>(
    name: &Ident,
    variant: &'a Variant,
    prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
    synstructure::match_pattern(
        &name,
        &variant.data,
        &BindOpts::with_prefix(BindStyle::Move, prefix.to_owned()),
    )
}

pub fn variant_ctor<'a>(
    input: &'a DeriveInput,
    variant: &Variant,
) -> Cow<'a, Ident> {
    match input.body {
        Body::Struct(_) => Cow::Borrowed(&input.ident),
        Body::Enum(_) => {
            Cow::Owned(Ident::from(
                format!("{}::{}", input.ident, variant.ident),
            ))
        },
    }
}

pub fn variants(input: &DeriveInput) -> Cow<[Variant]> {
    match input.body {
        Body::Enum(ref variants) => (&**variants).into(),
        Body::Struct(ref data) => {
            vec![Variant {
                ident: input.ident.clone(),
                attrs: input.attrs.clone(),
                data: data.clone(),
                discriminant: None,
            }].into()
        },
    }
}

pub fn where_predicate(
    bounded_ty: Ty,
    trait_path: &[&str],
    trait_output: Option<(&str, Ty)>,
) -> WherePredicate {
    WherePredicate::BoundPredicate(WhereBoundPredicate {
        bound_lifetimes: vec![],
        bounded_ty,
        bounds: vec![TyParamBound::Trait(
            PolyTraitRef {
                bound_lifetimes: vec![],
                trait_ref: trait_ref(trait_path, trait_output),
            },
            TraitBoundModifier::None
        )],
    })
}

/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz" or "Webkit", the result string
/// is prepended with "-".
pub fn to_css_identifier(mut camel_case: &str) -> String {
    camel_case = camel_case.trim_right_matches('_');
    let mut first = true;
    let mut result = String::with_capacity(camel_case.len());
    while let Some(segment) = split_camel_segment(&mut camel_case) {
        if first {
            match segment {
                "Moz" | "Webkit" => first = false,
                _ => {},
            }
        }
        if !first {
            result.push_str("-");
        }
        first = false;
        result.push_str(&segment.to_lowercase());
    }
    result
}

/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
    let index = match camel_case.chars().next() {
        None => return None,
        Some(ch) => ch.len_utf8(),
    };
    let end_position = camel_case[index..]
        .find(char::is_uppercase)
        .map_or(camel_case.len(), |pos| index + pos);
    let result = &camel_case[..end_position];
    *camel_case = &camel_case[end_position..];
    Some(result)
}
<commit_msg>style: Handle servo-prefixed stuff correctly.<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/. */

use darling::{FromDeriveInput, FromField, FromVariant};
use quote::{ToTokens, Tokens};
use std::borrow::Cow;
use std::collections::HashSet;
use std::iter;
use syn::{self, AngleBracketedParameterData, Body, DeriveInput, Field, Ident};
use syn::{ImplGenerics, Path, PathParameters, PathSegment, PolyTraitRef};
use syn::{QSelf, TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound};
use syn::{TypeBinding, Variant, WhereBoundPredicate, WherePredicate};
use syn::visit::{self, Visitor};
use synstructure::{self, BindOpts, BindStyle, BindingInfo};

pub struct WhereClause<'input, 'path> {
    pub inner: syn::WhereClause,
    pub params: &'input [TyParam],
    trait_path: &'path [&'path str],
    trait_output: Option<&'path str>,
    bounded_types: HashSet<Ty>,
}

impl<'input, 'path> ToTokens for WhereClause<'input, 'path> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        self.inner.to_tokens(tokens);
    }
}

impl<'input, 'path> WhereClause<'input, 'path> {
    pub fn add_trait_bound(&mut self, ty: &Ty) {
        let trait_path = self.trait_path;
        let params = self.params;
        let mut found = self.trait_output.map(|_| HashSet::new());
        if self.bounded_types.contains(&ty) {
            return;
        }
        if !is_parameterized(&ty, params, found.as_mut()) {
            return;
        }
        self.bounded_types.insert(ty.clone());

        let output = if let Some(output) = self.trait_output {
            output
        } else {
            self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
            return;
        };

        if let Ty::Path(None, ref path) = *ty {
            if path_to_ident(path).is_some() {
                self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None));
                return;
            }
        }

        let output_type = map_type_params(ty, params, &mut |ident| {
            let ty = Ty::Path(None, ident.clone().into());
            fmap_output_type(ty, trait_path, output)
        });

        let pred = where_predicate(
            ty.clone(),
            trait_path,
            Some((output, output_type)),
        );

        self.inner.predicates.push(pred);

        if let Some(found) = found {
            for ident in found {
                let ty = Ty::Path(None, ident.into());
                if !self.bounded_types.contains(&ty) {
                    self.bounded_types.insert(ty.clone());
                    self.inner.predicates.push(
                        where_predicate(ty, trait_path, None),
                    );
                };
            }
        }
    }
}

pub fn fmap_match<F>(
    input: &DeriveInput,
    bind_style: BindStyle,
    mut f: F,
) -> Tokens
where
    F: FnMut(BindingInfo) -> Tokens,
{
    synstructure::each_variant(input, &bind_style.into(), |fields, variant| {
        let name = variant_ctor(input, variant);
        let (mapped, mapped_fields) = value(&name, variant, "mapped");
        let fields_pairs = fields.into_iter().zip(mapped_fields);
        let mut computations = quote!();
        computations.append_all(fields_pairs.map(|(field, mapped_field)| {
            let expr = f(field);
            quote! { let #mapped_field = #expr; }
        }));
        computations.append(mapped);
        Some(computations)
    })
}

fn fmap_output_type(
    ty: Ty,
    trait_path: &[&str],
    trait_output: &str,
) -> Ty {
    Ty::Path(
        Some(QSelf {
            ty: Box::new(ty),
            position: trait_path.len(),
        }),
        path(trait_path.iter().chain(iter::once(&trait_output))),
    )
}

pub fn fmap_trait_parts<'input, 'path>(
    input: &'input DeriveInput,
    trait_path: &'path [&'path str],
    trait_output: &'path str,
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>, Path) {
    let (impl_generics, ty_generics, mut where_clause) = trait_parts(input, trait_path);
    where_clause.trait_output = Some(trait_output);
    let output_ty = PathSegment {
        ident: input.ident.clone(),
        parameters: PathParameters::AngleBracketed(AngleBracketedParameterData {
            lifetimes: input.generics.lifetimes.iter().map(|l| l.lifetime.clone()).collect(),
            types: input.generics.ty_params.iter().map(|ty| {
                fmap_output_type(
                    Ty::Path(None, ty.ident.clone().into()),
                    trait_path,
                    trait_output,
                )
            }).collect(),
            .. Default::default()
        }),
    }.into();
    (impl_generics, ty_generics, where_clause, output_ty)
}

pub fn is_parameterized(
    ty: &Ty,
    params: &[TyParam],
    found: Option<&mut HashSet<Ident>>,
) -> bool {
    struct IsParameterized<'a, 'b> {
        params: &'a [TyParam],
        has_free: bool,
        found: Option<&'b mut HashSet<Ident>>,
    }

    impl<'a, 'b> Visitor for IsParameterized<'a, 'b> {
        fn visit_path(&mut self, path: &Path) {
            if let Some(ident) = path_to_ident(path) {
                if self.params.iter().any(|param| param.ident == ident) {
                    self.has_free = true;
                    if let Some(ref mut found) = self.found {
                        found.insert(ident.clone());
                    }
                }
            }
            visit::walk_path(self, path);
        }
    }

    let mut visitor = IsParameterized { params, has_free: false, found };
    visitor.visit_ty(ty);
    visitor.has_free
}

pub fn map_type_params<F>(ty: &Ty, params: &[TyParam], f: &mut F) -> Ty
where
    F: FnMut(&Ident) -> Ty,
{
    match *ty {
        Ty::Slice(ref ty) => Ty::Slice(Box::new(map_type_params(ty, params, f))),
        Ty::Array(ref ty, ref expr) => {
            Ty::Array(Box::new(map_type_params(ty, params, f)), expr.clone())
        },
        Ty::Never => Ty::Never,
        Ty::Tup(ref items) => {
            Ty::Tup(items.iter().map(|ty| map_type_params(ty, params, f)).collect())
        },
        Ty::Path(None, ref path) => {
            if let Some(ident) = path_to_ident(path) {
                if params.iter().any(|param| param.ident == ident) {
                    return f(ident);
                }
            }
            Ty::Path(None, map_type_params_in_path(path, params, f))
        }
        Ty::Path(ref qself, ref path) => {
            Ty::Path(
                qself.as_ref().map(|qself| {
                    QSelf {
                        ty: Box::new(map_type_params(&qself.ty, params, f)),
                        position: qself.position,
                    }
                }),
                map_type_params_in_path(path, params, f),
            )
        },
        Ty::Paren(ref ty) => Ty::Paren(Box::new(map_type_params(ty, params, f))),
        ref ty => panic!("type {:?} cannot be mapped yet", ty),
    }
}

fn map_type_params_in_path<F>(path: &Path, params: &[TyParam], f: &mut F) -> Path
where
    F: FnMut(&Ident) -> Ty,
{
    Path {
        global: path.global,
        segments: path.segments.iter().map(|segment| {
            PathSegment {
                ident: segment.ident.clone(),
                parameters: match segment.parameters {
                    PathParameters::AngleBracketed(ref data) => {
                        PathParameters::AngleBracketed(AngleBracketedParameterData {
                            lifetimes: data.lifetimes.clone(),
                            types: data.types.iter().map(|ty| {
                                map_type_params(ty, params, f)
                            }).collect(),
                            bindings: data.bindings.iter().map(|binding| {
                                TypeBinding {
                                    ident: binding.ident.clone(),
                                    ty: map_type_params(&binding.ty, params, f),
                                }
                            }).collect(),
                        })
                    },
                    ref parameters => {
                        panic!("parameters {:?} cannot be mapped yet", parameters)
                    },
                },
            }
        }).collect(),
    }
}

pub fn path<S>(segments: S) -> Path
where
    S: IntoIterator,
    <S as IntoIterator>::Item: AsRef<str>,
{
    Path {
        global: true,
        segments: segments.into_iter().map(|s| s.as_ref().into()).collect(),
    }
}

fn path_to_ident(path: &Path) -> Option<&Ident> {
    match *path {
        Path { global: false, ref segments } if segments.len() == 1 => {
            if segments[0].parameters.is_empty() {
                Some(&segments[0].ident)
            } else {
                None
            }
        },
        _ => None,
    }
}

pub fn parse_field_attrs<A>(field: &Field) -> A
where
    A: FromField,
{
    match A::from_field(field) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse field attributes: {}", e),
    }
}

pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
where
    A: FromDeriveInput,
{
    match A::from_derive_input(input) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse input attributes: {}", e),
    }
}

pub fn parse_variant_attrs<A>(variant: &Variant) -> A
where
    A: FromVariant,
{
    match A::from_variant(variant) {
        Ok(attrs) => attrs,
        Err(e) => panic!("failed to parse variant attributes: {}", e),
    }
}

pub fn ref_pattern<'a>(
    name: &Ident,
    variant: &'a Variant,
    prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
    synstructure::match_pattern(
        &name,
        &variant.data,
        &BindOpts::with_prefix(BindStyle::Ref, prefix.to_owned()),
    )
}

pub fn trait_parts<'input, 'path>(
    input: &'input DeriveInput,
    trait_path: &'path [&'path str],
) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>) {
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let where_clause = WhereClause {
        inner: where_clause.clone(),
        params: &input.generics.ty_params,
        trait_path,
        trait_output: None,
        bounded_types: HashSet::new()
    };
    (impl_generics, ty_generics, where_clause)
}

fn trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path {
    let (name, parent) = path.split_last().unwrap();
    let last_segment = PathSegment {
        ident: (*name).into(),
        parameters: PathParameters::AngleBracketed(
            AngleBracketedParameterData {
                bindings: output.into_iter().map(|(param, ty)| {
                    TypeBinding { ident: param.into(), ty }
                }).collect(),
                .. Default::default()
            }
        )
    };
    Path {
        global: true,
        segments: {
            parent
                .iter()
                .cloned()
                .map(Into::into)
                .chain(iter::once(last_segment))
                .collect()
        },
    }
}

pub fn value<'a>(
    name: &Ident,
    variant: &'a Variant,
    prefix: &str,
) -> (Tokens, Vec<BindingInfo<'a>>) {
    synstructure::match_pattern(
        &name,
        &variant.data,
        &BindOpts::with_prefix(BindStyle::Move, prefix.to_owned()),
    )
}

pub fn variant_ctor<'a>(
    input: &'a DeriveInput,
    variant: &Variant,
) -> Cow<'a, Ident> {
    match input.body {
        Body::Struct(_) => Cow::Borrowed(&input.ident),
        Body::Enum(_) => {
            Cow::Owned(Ident::from(
                format!("{}::{}", input.ident, variant.ident),
            ))
        },
    }
}

pub fn variants(input: &DeriveInput) -> Cow<[Variant]> {
    match input.body {
        Body::Enum(ref variants) => (&**variants).into(),
        Body::Struct(ref data) => {
            vec![Variant {
                ident: input.ident.clone(),
                attrs: input.attrs.clone(),
                data: data.clone(),
                discriminant: None,
            }].into()
        },
    }
}

pub fn where_predicate(
    bounded_ty: Ty,
    trait_path: &[&str],
    trait_output: Option<(&str, Ty)>,
) -> WherePredicate {
    WherePredicate::BoundPredicate(WhereBoundPredicate {
        bound_lifetimes: vec![],
        bounded_ty,
        bounds: vec![TyParamBound::Trait(
            PolyTraitRef {
                bound_lifetimes: vec![],
                trait_ref: trait_ref(trait_path, trait_output),
            },
            TraitBoundModifier::None
        )],
    })
}

/// Transforms "FooBar" to "foo-bar".
///
/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
/// is prepended with "-".
pub fn to_css_identifier(mut camel_case: &str) -> String {
    camel_case = camel_case.trim_right_matches('_');
    let mut first = true;
    let mut result = String::with_capacity(camel_case.len());
    while let Some(segment) = split_camel_segment(&mut camel_case) {
        if first {
            match segment {
                "Moz" | "Webkit" | "Servo" => first = false,
                _ => {},
            }
        }
        if !first {
            result.push_str("-");
        }
        first = false;
        result.push_str(&segment.to_lowercase());
    }
    result
}

/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
    let index = match camel_case.chars().next() {
        None => return None,
        Some(ch) => ch.len_utf8(),
    };
    let end_position = camel_case[index..]
        .find(char::is_uppercase)
        .map_or(camel_case.len(), |pos| index + pos);
    let result = &camel_case[..end_position];
    *camel_case = &camel_case[end_position..];
    Some(result)
}
<|endoftext|>


<commit_before>use std::result::Result;

use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;
use super::tool_name::*;
use super::functional_tool::*;
use super::dynamic_environment::*;
use super::static_environment::*;

///
/// Retrieves a tool, or returns a JSON error indicating it doesn't exist
///
#[inline]
fn get_tool(env: &Environment, tool_name: &str) -> Result<Box<Tool>, Value> {
    match env.get_json_tool(tool_name) {
        Ok(tool)            => Ok(tool),
        Err(retrieve_error) => Err(json![{
            "error":        "Tool not found",
            "tool_name":    tool_name,
            "description":  retrieve_error.message()
        }])
    }
}

///
/// Calls the define action for a tool in this environment
///
pub fn define_new_tool(environment: &Environment, new_tool_name: &str, tool: Box<Tool>) -> Result<(), Value> {
    // Fetch the define tool for the currnet environment
    let define_tool         = TypedTool::<DefineToolInput, ()>::from(get_tool(environment, DEFINE_TOOL)?);

    // Put the tool we want to define in its own environment so we can pass it to the define tool
    let source_environment  = StaticEnvironment::from_tool(new_tool_name, tool);

    // Define this tool
    define_tool.invoke(DefineToolInput::new(new_tool_name, Some(new_tool_name)), &source_environment)?;

    Ok(())
}

///
/// Redefines a tool in this environment
///
pub fn alias_tool(environment: &Environment, old_tool_name: &str, new_tool_name: &str) -> Result<(), Value> {
    // Fetch the define tool for the currnet environment
    let define_tool = TypedTool::<DefineToolInput, ()>::from(get_tool(environment, DEFINE_TOOL)?);

    // Perform aliasing
    define_tool.invoke(DefineToolInput::new(old_tool_name, Some(new_tool_name)), environment)?;

    Ok(())
}

///
/// Undefines a tool in this environment
///
pub fn undefine_tool(environment: &Environment, old_tool_name: &str) -> Result<bool, Value> {
    // Fetch the define tool for the currnet environment
    let undefine_tool = TypedTool::<UndefineToolInput, bool>::from(get_tool(environment, UNDEFINE_TOOL)?);

    // Remove the tool
    Ok(undefine_tool.invoke(UndefineToolInput::new(old_tool_name), environment)?)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn can_define_new_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        assert!(env.get_json_tool("test").is_err());
        assert!(define_new_tool(&env, "test", Box::new(new_tool)).is_ok());
        assert!(env.get_json_tool("test").is_ok());
    }

    #[test]
    fn can_alias_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        env.define("test", Box::new(new_tool));

        assert!(env.get_json_tool("test").is_ok());
        assert!(env.get_json_tool("test2").is_err());
        assert!(alias_tool(&env, "test", "test2").is_ok());
        assert!(env.get_json_tool("test2").is_ok());
    }

    #[test]
    fn can_undefine_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        env.define("test", Box::new(new_tool));

        assert!(env.get_json_tool("test").is_ok());
        assert!(undefine_tool(&env, "test") == Ok(true));
        assert!(env.get_json_tool("test").is_err());
    }
}
<commit_msg>Add some convenience functions for defining tools directly from functions<commit_after>use std::result::Result;

use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;
use super::tool_name::*;
use super::functional_tool::*;
use super::dynamic_environment::*;
use super::static_environment::*;

///
/// Retrieves a tool, or returns a JSON error indicating it doesn't exist
///
#[inline]
fn get_tool(env: &Environment, tool_name: &str) -> Result<Box<Tool>, Value> {
    match env.get_json_tool(tool_name) {
        Ok(tool)            => Ok(tool),
        Err(retrieve_error) => Err(json![{
            "error":        "Tool not found",
            "tool_name":    tool_name,
            "description":  retrieve_error.message()
        }])
    }
}

///
/// Calls the define action for a tool in this environment
///
pub fn define_new_tool(environment: &Environment, new_tool_name: &str, tool: Box<Tool>) -> Result<(), Value> {
    // Fetch the define tool for the currnet environment
    let define_tool         = TypedTool::<DefineToolInput, ()>::from(get_tool(environment, DEFINE_TOOL)?);

    // Put the tool we want to define in its own environment so we can pass it to the define tool
    let source_environment  = StaticEnvironment::from_tool(new_tool_name, tool);

    // Define this tool
    define_tool.invoke(DefineToolInput::new(new_tool_name, Some(new_tool_name)), &source_environment)?;

    Ok(())
}

///
/// Redefines a tool in this environment
///
pub fn alias_tool(environment: &Environment, old_tool_name: &str, new_tool_name: &str) -> Result<(), Value> {
    // Fetch the define tool for the currnet environment
    let define_tool = TypedTool::<DefineToolInput, ()>::from(get_tool(environment, DEFINE_TOOL)?);

    // Perform aliasing
    define_tool.invoke(DefineToolInput::new(old_tool_name, Some(new_tool_name)), environment)?;

    Ok(())
}

///
/// Undefines a tool in this environment
///
pub fn undefine_tool(environment: &Environment, old_tool_name: &str) -> Result<bool, Value> {
    // Fetch the define tool for the currnet environment
    let undefine_tool = TypedTool::<UndefineToolInput, bool>::from(get_tool(environment, UNDEFINE_TOOL)?);

    // Remove the tool
    Ok(undefine_tool.invoke(UndefineToolInput::new(old_tool_name), environment)?)
}

///
/// Defines a new tool from a function that doesn't produce errors in this environment
///
pub fn define_pure_tool<TIn: 'static, TOut: 'static+Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(environment: &Environment, new_tool_name: &str, function: F) -> Result<(), Value>
where for<'de> TIn: Deserialize<'de> {
    define_new_tool(environment, new_tool_name, Box::new(make_pure_tool(function)))
}

///
/// Defines a new tool from a function that can produce errors in this environment
///
pub fn define_dynamic_tool<TIn: 'static, TOut: 'static+Serialize, TErr: 'static+Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(environment: &Environment, new_tool_name: &str, function: F) -> Result<(), Value>
where for<'de> TIn: Deserialize<'de> {
    define_new_tool(environment, new_tool_name, Box::new(make_dynamic_tool(function)))
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn can_define_new_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        assert!(env.get_json_tool("test").is_err());
        assert!(define_new_tool(&env, "test", Box::new(new_tool)).is_ok());
        assert!(env.get_json_tool("test").is_ok());
    }

    #[test]
    fn can_define_pure_tool_using_convenience_function() {
        let env      = DynamicEnvironment::new();

        assert!(env.get_json_tool("test").is_err());
        assert!(define_pure_tool(&env, "test", |x: i32| x+1).is_ok());
        assert!(env.get_json_tool("test").is_ok());
    }

    #[test]
    fn can_define_dynamic_tool_using_convenience_function() {
        let env      = DynamicEnvironment::new();

        assert!(env.get_json_tool("test").is_err());
        assert!(define_dynamic_tool(&env, "test", |x: i32, _env| if x > 0 { Ok(x-1) } else { Err(false) }).is_ok());
        assert!(env.get_json_tool("test").is_ok());
    }

    #[test]
    fn can_alias_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        env.define("test", Box::new(new_tool));

        assert!(env.get_json_tool("test").is_ok());
        assert!(env.get_json_tool("test2").is_err());
        assert!(alias_tool(&env, "test", "test2").is_ok());
        assert!(env.get_json_tool("test2").is_ok());
    }

    #[test]
    fn can_undefine_tool_using_convenience_function() {
        let new_tool = make_pure_tool(|x: i32| x+1);
        let env      = DynamicEnvironment::new();

        env.define("test", Box::new(new_tool));

        assert!(env.get_json_tool("test").is_ok());
        assert!(undefine_tool(&env, "test") == Ok(true));
        assert!(env.get_json_tool("test").is_err());
    }
}
<|endoftext|>


<commit_before>use regex::Regex;

use hyper::Client;
use hyper::client::Body;

use serde_json::{to_string, from_reader, Map, Value};

use errors::HueError;
use ::hue::*;

/// Attempt to discover bridges using `https://www.meethue.com/api/nupnp`
pub fn discover() -> Result<Vec<Discovery>, HueError> {
    let client = Client::new();

    let mut res = try!(client.get("https://www.meethue.com/api/nupnp").send());

    from_reader(&mut res).map_err(From::from)
}

/// A builder object for a `Bridge` object
#[derive(Debug)]
pub struct BridgeBuilder{
    ip: String
}

impl BridgeBuilder{
    /// Starts building a `Bridge` from the given IP
    pub fn from_ip(ip: String) -> Self{
        BridgeBuilder{
            ip: ip
        }
    }
    /// Returns a `Bridge` from an already existing user
    pub fn from_username(self, username: String) -> Bridge {
        let BridgeBuilder{ip} = self;
        Bridge {
            client: Client::new(),
            username: username,
            ip: ip
        }
    }
    /// Registers a new user on the bridge
    pub fn register_user(self, devicetype: &str) -> RegisterIter{
        RegisterIter(Some(self), devicetype)
    }
}

#[derive(Debug)]
/// Iterator that tries to register a new user each iteration
///
/// It will most likely respond with an error saying that the link button needs to be pressed the first time
///
/// ## Example
/// ```no_run
/// use philipshue::errors::{HueError, BridgeError};
///
/// let mut bridge = None;
/// // Discover a bridge
/// let discovery = philipshue::bridge::discover().unwrap().pop().unwrap();
/// let devicetype = "my_hue_app#iphone";
///
/// // Keep trying to register a user
/// for res in discovery.build_bridge().register_user(devicetype){
///     match res{
///         // A new user has succesfully been registered and a `Bridge` object is returned
///         Ok(r) => {
///             bridge = Some(r);
///         },
///         // Prompt the user to press the link button
///         Err(HueError::BridgeError{error: BridgeError::LinkButtonNotPressed, ..}) => {
///             println!("Please, press the link on the bridge. Retrying in 5 seconds");
///             std::thread::sleep(std::time::Duration::from_secs(5));
///         },
///         // Some other error happened
///         Err(e) => {
///             println!("Unexpected error occured: {:?}", e);
///             break
///         }
///     }
/// }
/// ```
pub struct RegisterIter<'a>(Option<BridgeBuilder>, &'a str);

impl<'a> Iterator for RegisterIter<'a> {
    type Item = Result<Bridge, HueError>;
    fn next(&mut self) -> Option<Self::Item>{
        if let Some(bb) = ::std::mem::replace(&mut self.0, None){
            let client = Client::new();

            let body = format!("{{\"devicetype\": {:?}}}", self.1);
            let body = body.as_bytes();
            let url = format!("http://{}/api", bb.ip);
            let mut resp = match client.post(&url)
                .body(Body::BufBody(body, body.len()))
                .send() {
                    Ok(r) => r,
                    Err(e) => return Some(Err(HueError::from(e)))
                };

            let rur = match from_reader::<_, Vec<HueResponse<User>>>(&mut resp) {
                Ok(mut r) => r.pop().unwrap(),
                Err(e) => return Some(Err(HueError::from(e)))
            };

            Some(if let Some(User{username}) = rur.success{
                let BridgeBuilder{ip} = bb;

                Ok(Bridge{
                    ip: ip,
                    client: client,
                    username: username
                })
            }else if let Some(error) = rur.error{
                self.0 = Some(bb);
                Err(error.into())
            }else{
                Err(HueError::MalformedResponse)
            })
        }else{
            None
        }
    }
}

#[derive(Debug)]
/// The bridge connection
pub struct Bridge {
    client: Client,
    /// The IP address of the bridge
    pub ip: String,
    /// The username for the user on the bridge
    pub username: String,
}

impl Bridge {
    /// Gets all lights from the bridge
    pub fn get_all_lights(&self) -> Result<Vec<IdentifiedLight>, HueError> {
        let url = format!("http://{}/api/{}/lights",
                          self.ip,
                          self.username);

        let mut resp = try!(self.client.get(&url).send());
        let json: Map<usize, Light> = try!(from_reader(&mut resp));

        let mut lights: Vec<IdentifiedLight> = try!(json.into_iter()
            .map(|(id, light)| -> Result<IdentifiedLight, HueError> {
                Ok(IdentifiedLight {
                    id: id,
                    light: light,
                })
            })
            .collect());
        lights.sort_by_key(|x| x.id);
        Ok(lights)
    }
    /// Sends a `LightCommand` to set the state of a light
    pub fn set_light_state(&self, light: usize, command: LightCommand) -> Result<Value, HueError> {
        let url = format!("http://{}/api/{}/lights/{}/state",
                          self.ip,
                          self.username,
                          light);
        let body = try!(to_string(&command));
        let re1 = Regex::new("\"[a-z]*\":null,?").unwrap();
        let cleaned1 = re1.replace_all(&body, "");
        let re2 = Regex::new(",\\}").unwrap();
        let cleaned2 = re2.replace_all(&cleaned1, "}");
        let body = cleaned2.as_bytes();

        let mut resp = try!(self.client.put(&url)
            .body(Body::BufBody(body, body.len()))
            .send());

        from_reader(&mut resp).map_err(From::from)
    }
}
<commit_msg>More functional style<commit_after>use regex::Regex;

use hyper::Client;
use hyper::client::Body;

use serde_json::{to_string, from_reader, Map, Value};

use errors::HueError;
use ::hue::*;

/// Attempt to discover bridges using `https://www.meethue.com/api/nupnp`
pub fn discover() -> Result<Vec<Discovery>, HueError> {
    Client::new()
        .get("https://www.meethue.com/api/nupnp")
        .send()
        .map_err(HueError::from)
        .and_then(|ref mut r| from_reader(r).map_err(From::from))
}

/// A builder object for a `Bridge` object
#[derive(Debug)]
pub struct BridgeBuilder{
    ip: String
}

impl BridgeBuilder{
    /// Starts building a `Bridge` from the given IP
    pub fn from_ip(ip: String) -> Self{
        BridgeBuilder{
            ip: ip
        }
    }
    /// Returns a `Bridge` from an already existing user
    pub fn from_username(self, username: String) -> Bridge {
        let BridgeBuilder{ip} = self;
        Bridge {
            client: Client::new(),
            username: username,
            ip: ip
        }
    }
    /// Registers a new user on the bridge
    pub fn register_user(self, devicetype: &str) -> RegisterIter{
        RegisterIter(Some(self), devicetype)
    }
}

#[derive(Debug)]
/// Iterator that tries to register a new user each iteration
///
/// It will most likely respond with an error saying that the link button needs to be pressed the first time
///
/// ## Example
/// ```no_run
/// use philipshue::errors::{HueError, BridgeError};
///
/// let mut bridge = None;
/// // Discover a bridge
/// let discovery = philipshue::bridge::discover().unwrap().pop().unwrap();
/// let devicetype = "my_hue_app#iphone";
///
/// // Keep trying to register a user
/// for res in discovery.build_bridge().register_user(devicetype){
///     match res{
///         // A new user has succesfully been registered and a `Bridge` object is returned
///         Ok(r) => {
///             bridge = Some(r);
///         },
///         // Prompt the user to press the link button
///         Err(HueError::BridgeError{error: BridgeError::LinkButtonNotPressed, ..}) => {
///             println!("Please, press the link on the bridge. Retrying in 5 seconds");
///             std::thread::sleep(std::time::Duration::from_secs(5));
///         },
///         // Some other error happened
///         Err(e) => {
///             println!("Unexpected error occured: {:?}", e);
///             break
///         }
///     }
/// }
/// ```
pub struct RegisterIter<'a>(Option<BridgeBuilder>, &'a str);

impl<'a> Iterator for RegisterIter<'a> {
    type Item = Result<Bridge, HueError>;
    fn next(&mut self) -> Option<Self::Item>{
        if let Some(bb) = ::std::mem::replace(&mut self.0, None){
            let client = Client::new();

            let body = format!("{{\"devicetype\": {:?}}}", self.1);
            let body = body.as_bytes();
            let url = format!("http://{}/api", bb.ip);
            let mut resp = match client.post(&url)
                .body(Body::BufBody(body, body.len()))
                .send() {
                    Ok(r) => r,
                    Err(e) => return Some(Err(HueError::from(e)))
                };

            let rur = match from_reader::<_, Vec<HueResponse<User>>>(&mut resp) {
                Ok(mut r) => r.pop().unwrap(),
                Err(e) => return Some(Err(HueError::from(e)))
            };

            Some(if let Some(User{username}) = rur.success{
                let BridgeBuilder{ip} = bb;

                Ok(Bridge{
                    ip: ip,
                    client: client,
                    username: username
                })
            }else if let Some(error) = rur.error{
                self.0 = Some(bb);
                Err(error.into())
            }else{
                Err(HueError::MalformedResponse)
            })
        }else{
            None
        }
    }
}

#[derive(Debug)]
/// The bridge connection
pub struct Bridge {
    client: Client,
    /// The IP address of the bridge
    pub ip: String,
    /// The username for the user on the bridge
    pub username: String,
}

impl Bridge {
    /// Gets all lights from the bridge
    pub fn get_all_lights(&self) -> Result<Vec<IdentifiedLight>, HueError> {
        self.client
            .get(&format!("http://{}/api/{}/lights", self.ip, self.username))
            .send()
            .map_err(HueError::from)
            .and_then(|ref mut resp| from_reader::<_, Map<usize, Light>>(resp).map_err(From::from))
            .map(|json: Map<usize, Light>|{
                let mut lights: Vec<_> = json
                    .into_iter()
                    .map(|(id, light)| {
                        IdentifiedLight {
                            id: id,
                            light: light,
                        }
                    })
                    .collect();
                lights.sort_by_key(|x| x.id);
                lights
            })
    }
    /// Sends a `LightCommand` to set the state of a light
    pub fn set_light_state(&self, light: usize, command: LightCommand) -> Result<Value, HueError> {
        let url = format!("http://{}/api/{}/lights/{}/state",
                          self.ip,
                          self.username,
                          light);
        let body = try!(to_string(&command));
        let re1 = Regex::new("\"[a-z]*\":null,?").unwrap();
        let cleaned1 = re1.replace_all(&body, "");
        let re2 = Regex::new(",\\}").unwrap();
        let cleaned2 = re2.replace_all(&cleaned1, "}");
        let body = cleaned2.as_bytes();

        self.client
            .put(&url)
            .body(Body::BufBody(body, body.len()))
            .send()
            .map_err(HueError::from)
            .and_then(|ref mut resp| from_reader(resp).map_err(From::from))
    }
}
<|endoftext|>