<commit_before>use std::net::{SocketAddr, SocketAddrV4};
use std::str::FromStr;
use std::io::{Read, Write, Result};
use mio::tcp::{TcpSocket, TcpStream};
use mioco::{MiocoHandle, MailboxOuterEnd, EventSource};
use mioco;
use std::sync::{Arc, RwLock};
use config::Config;

/// The NetworkHandler manages all the networking, connections, requests, etc
/// There technically isn't a need for a struct -- we could have used a function --
/// but I like the syntax of static struct methods :)
pub struct NetworkHandler;

impl NetworkHandler {

    /// Start spinning the networking coroutine.  This attempts to bind itself
    /// to the configured host:port, then start listening.  When connections are accepted,
    /// it will dispatch the connection to a new coroutine
    pub fn start(mioco: &mut MiocoHandle, config: Arc<RwLock<Config>>) -> Result<()> {

        // Obtain a read-lock, make a copy of host/port so we can release the lock
        // This is the only thread that requires that data, so it is safe to cache
        // a local copy
        let (bind_host, bind_port) = {
            let reader = config.read().unwrap();
            (&reader.discovery.bind_host.clone(), reader.discovery.bind_port.clone())
        };

        let mut addr = build_address(bind_host, bind_port);
        info!("Binding {}...", addr);

        // We'll replace all this with proper error handling in the future
        let sock = try!(TcpSocket::v4());

        // Try to bind the requested host:port, but if the port is taken,
        // keep incrementing until we find one
        loop {
            match sock.bind(&addr) {
                Ok(_) => break,
                Err(_) => {
                    error!("Port [{}] taken, trying higher", addr.port());
                    addr = build_address(bind_host, addr.port() + 1);
                }
            }
        }
        let listener = try!(sock.listen(1024));

        // If we had to choose a new port, obtain a write-lock and update the config
        if addr.port() != bind_port {
            let mut c = config.write().unwrap();
            c.discovery.bind_port = addr.port();
            debug!("Updating config to port: {}", addr.port());
        }

        info!("Server bound on {:?}", listener.local_addr().unwrap());

        // Spin up the discovery connections
        start_discovery(mioco, &config);

        // To allow mioco to block coroutines without blocking the thread,
        // we have to wrap all mio constructs first
        let listener = mioco.wrap(listener);

        // Accept connections forever
        loop {
            let conn = try!(listener.accept());

            // If we get one, spawn a new coroutine and go back to
            // listening for more connections
            mioco.spawn(move |mioco| {
                connection(mioco, conn)
            });
        }
    }
}

fn build_address(bind_host: &String, bind_port: u16) -> SocketAddr {
    let ip = FromStr::from_str(&*bind_host)
                .unwrap_or_else(|err|panic!("{}: [{}]", err, bind_host));;
    SocketAddr::V4(SocketAddrV4::new(ip, bind_port))
}

/// Spins a connection coroutine
/// Currently it just acts as an echo server, but in the near future
/// this will be responsible for pulling Cap'n'proto commands off the
/// line and dispatching to worker threads
fn connection(mioco: &mut MiocoHandle, conn: TcpStream) -> Result<()> {
    let peer_addr = conn.peer_addr().unwrap();

    debug!("Accepting connection from [{}]", peer_addr);

    let mut conn = mioco.wrap(conn);
    let mut buf = [0u8; 1024 * 16];

    // Simple echo server loop.  Read down all the data and
    // write it back
    loop {
        let size = try!(conn.read(&mut buf));
        debug!("Read [{}] bytes from [{}]", size, peer_addr);
        if size == 0 {
            /* eof */
            debug!("HUP from [{}]", peer_addr);
            break;
        }
        debug!("Writing [{}] bytes to [{}]", size, peer_addr);
        try!(conn.write_all(&mut buf[0..size]))
    }

    Ok(())
}

/// Begin the discovery process.  Essentially it iterates over the list of
/// discovery nodes and spawns a coroutine to talk to each one
fn start_discovery(mioco: &mut MiocoHandle, config: &Arc<RwLock<Config>>) {

    // We need to avoid connecting to ourself, so read-lock the config
    // and format bind_host appropriately.  We could do this below, but by
    // separating it out we give other threads a chance to jump in with a write-lock
    let bind_host = {
        let reader = config.read().unwrap();
        format!("{}:{}", &reader.discovery.bind_host, &reader.discovery.bind_port)
    };

    // Try to connect to external servers, but filter out ourself
    // Obtains a read-lock on the config for the duration of the iterator
    {
        let reader = config.read().unwrap();
        for host in reader.discovery.hosts.iter().filter(|&x| x != &*bind_host) {
            let addr: SocketAddr = FromStr::from_str(&*host).unwrap_or_else(|err|panic!("{}: [{}]", err, host));
            debug!("Connecting to external node [{}]...", addr);
            mioco.spawn(move |mioco| {
                discovery(mioco, addr)
            });
        }
    }
}

/// This function is in charge of talking to a single external node, attempting to
/// establish a connection, etc
fn discovery(mioco: &mut MiocoHandle, addr: SocketAddr) -> Result<()> {
    // Never gonna give you up...
    loop {

        // Create a socket and connect to our remote host
        let socket = TcpSocket::v4().unwrap();

        // Mio returns a stream and a completion flag.  If `complete` is
        // true, the socket connected immediately and we can start writing.
        // It usually returns false
        let (stream, complete) = socket.connect(&addr).unwrap();
        let stream = mioco.wrap(stream);

        // If the socket didn't connect immediately, block this coroutine
        // until we get a Writeable event (we've only initiated one pending IO, so
        // safe to use select_write() instead of select_write_from() )
        if !complete {
            let event = mioco.select_write();
            debug!("{:?}", event);
        }

        // We will use the mailboxes to notify our discovery thread if
        // the remote peer fails at some point in the future, so we can
        // can try to reconnect
        let (mail_send, mail_recv) = mioco::mailbox::<bool>();

        // If the socket connected successfully, or there are no errors...
        let success = complete | {
            match stream.with_raw(|s| s.take_socket_error()) {
                Ok(_) => true,
                Err(e) => {
                    debug!("err: {:?}", e);
                    false
                }
            }
        };

        // ... spawn a thread to handle talking to the remote node
        if success {
            mioco.spawn(move |mioco| {
                remote_node_handler(mioco, stream, mail_send)
            });

            // And then block this coroutine waiting for a failure notification
            let mut mail_recv = mioco.wrap(mail_recv);
            let failed = mail_recv.recv();
            debug!("Remote connection failed, sleep and try to reconnect...");
        }

        // Sleep for a bit, then try to re-connect
        mioco.sleep(2000);
    }
}

/// This function handles the communication with a healthy, connected node.
/// If the node fails, this function will signal via a mailbox and then exit
fn remote_node_handler(mioco: &mut MiocoHandle, mut stream: EventSource<TcpStream>,
                    mail_send: MailboxOuterEnd<bool>) -> Result<()> {
    loop {
        try!(stream.write("test".as_bytes()));
        debug!("Wrote [{}] bytes to remote connection", 4);
        mioco.sleep(5000);
        break;
    }

    debug!("Shutting down remote connection...");
    mail_send.send(true);
    Ok(())
}
<commit_msg>Rearrange error polling to be less confusing<commit_after>use std::net::{SocketAddr, SocketAddrV4};
use std::str::FromStr;
use std::io::{Read, Write, Result};
use mio::tcp::{TcpSocket, TcpStream};
use mioco::{MiocoHandle, MailboxOuterEnd, EventSource};
use mioco;
use std::sync::{Arc, RwLock};
use config::Config;

/// The NetworkHandler manages all the networking, connections, requests, etc
/// There technically isn't a need for a struct -- we could have used a function --
/// but I like the syntax of static struct methods :)
pub struct NetworkHandler;

impl NetworkHandler {

    /// Start spinning the networking coroutine.  This attempts to bind itself
    /// to the configured host:port, then start listening.  When connections are accepted,
    /// it will dispatch the connection to a new coroutine
    pub fn start(mioco: &mut MiocoHandle, config: Arc<RwLock<Config>>) -> Result<()> {

        // Obtain a read-lock, make a copy of host/port so we can release the lock
        // This is the only thread that requires that data, so it is safe to cache
        // a local copy
        let (bind_host, bind_port) = {
            let reader = config.read().unwrap();
            (&reader.discovery.bind_host.clone(), reader.discovery.bind_port.clone())
        };

        let mut addr = build_address(bind_host, bind_port);
        info!("Binding {}...", addr);

        // We'll replace all this with proper error handling in the future
        let sock = try!(TcpSocket::v4());

        // Try to bind the requested host:port, but if the port is taken,
        // keep incrementing until we find one
        loop {
            match sock.bind(&addr) {
                Ok(_) => break,
                Err(_) => {
                    error!("Port [{}] taken, trying higher", addr.port());
                    addr = build_address(bind_host, addr.port() + 1);
                }
            }
        }
        let listener = try!(sock.listen(1024));

        // If we had to choose a new port, obtain a write-lock and update the config
        if addr.port() != bind_port {
            let mut c = config.write().unwrap();
            c.discovery.bind_port = addr.port();
            debug!("Updating config to port: {}", addr.port());
        }

        info!("Server bound on {:?}", listener.local_addr().unwrap());

        // Spin up the discovery connections
        start_discovery(mioco, &config);

        // To allow mioco to block coroutines without blocking the thread,
        // we have to wrap all mio constructs first
        let listener = mioco.wrap(listener);

        // Accept connections forever
        loop {
            let conn = try!(listener.accept());

            // If we get one, spawn a new coroutine and go back to
            // listening for more connections
            mioco.spawn(move |mioco| {
                connection(mioco, conn)
            });
        }
    }
}

fn build_address(bind_host: &String, bind_port: u16) -> SocketAddr {
    let ip = FromStr::from_str(&*bind_host)
                .unwrap_or_else(|err|panic!("{}: [{}]", err, bind_host));;
    SocketAddr::V4(SocketAddrV4::new(ip, bind_port))
}

/// Spins a connection coroutine
/// Currently it just acts as an echo server, but in the near future
/// this will be responsible for pulling Cap'n'proto commands off the
/// line and dispatching to worker threads
fn connection(mioco: &mut MiocoHandle, conn: TcpStream) -> Result<()> {
    let peer_addr = conn.peer_addr().unwrap();

    debug!("Accepting connection from [{}]", peer_addr);

    let mut conn = mioco.wrap(conn);
    let mut buf = [0u8; 1024 * 16];

    // Simple echo server loop.  Read down all the data and
    // write it back
    loop {
        let size = try!(conn.read(&mut buf));
        debug!("Read [{}] bytes from [{}]", size, peer_addr);
        if size == 0 {
            /* eof */
            debug!("HUP from [{}]", peer_addr);
            break;
        }
        debug!("Writing [{}] bytes to [{}]", size, peer_addr);
        try!(conn.write_all(&mut buf[0..size]))
    }

    Ok(())
}

/// Begin the discovery process.  Essentially it iterates over the list of
/// discovery nodes and spawns a coroutine to talk to each one
fn start_discovery(mioco: &mut MiocoHandle, config: &Arc<RwLock<Config>>) {

    // We need to avoid connecting to ourself, so read-lock the config
    // and format bind_host appropriately.  We could do this below, but by
    // separating it out we give other threads a chance to jump in with a write-lock
    let bind_host = {
        let reader = config.read().unwrap();
        format!("{}:{}", &reader.discovery.bind_host, &reader.discovery.bind_port)
    };

    // Try to connect to external servers, but filter out ourself
    // Obtains a read-lock on the config for the duration of the iterator
    {
        let reader = config.read().unwrap();
        for host in reader.discovery.hosts.iter().filter(|&x| x != &*bind_host) {
            let addr: SocketAddr = FromStr::from_str(&*host).unwrap_or_else(|err|panic!("{}: [{}]", err, host));
            debug!("Connecting to external node [{}]...", addr);
            mioco.spawn(move |mioco| {
                discovery(mioco, addr)
            });
        }
    }
}

/// This function is in charge of talking to a single external node, attempting to
/// establish a connection, etc
fn discovery(mioco: &mut MiocoHandle, addr: SocketAddr) -> Result<()> {
    // Never gonna give you up...
    loop {

        // Create a socket and connect to our remote host
        let socket = TcpSocket::v4().unwrap();

        // Mio returns a stream and a completion flag.  If `complete` is
        // true, the socket connected immediately and we can start writing.
        // It usually returns false
        let (stream, mut complete) = socket.connect(&addr).unwrap();
        let stream = mioco.wrap(stream);

        // If the socket didn't connect immediately, connect_wait() will
        // block this coroutine and return a Result to signal if the connection
        // completed
        if !complete {
            mioco.select_write();
            complete = match stream.with_raw(|s| s.take_socket_error()) {
                Ok(_) => true,
                Err(e) => {
                    debug!("err: {:?}", e);
                    false
                }
            }
        }

        // ... spawn a thread to handle talking to the remote node
        if complete {

            // We will use the mailboxes to notify our discovery thread if
            // the remote peer fails at some point in the future, so we can
            // can try to reconnect
            let (mail_send, mail_recv) = mioco::mailbox::<bool>();

            mioco.spawn(move |mioco| {
                remote_node_handler(mioco, stream, mail_send)
            });

            // And then block this coroutine waiting for a failure notification
            let mut mail_recv = mioco.wrap(mail_recv);
            let failed = mail_recv.recv();
            debug!("Remote connection failed, sleep and try to reconnect...");
        }

        // Sleep for a bit, then try to re-connect
        mioco.sleep(2000);
    }
}

/// This function handles the communication with a healthy, connected node.
/// If the node fails, this function will signal via a mailbox and then exit
fn remote_node_handler(mioco: &mut MiocoHandle, mut stream: EventSource<TcpStream>,
                    mail_send: MailboxOuterEnd<bool>) -> Result<()> {
    loop {
        try!(stream.write("test".as_bytes()));
        debug!("Wrote [{}] bytes to remote connection", 4);
        mioco.sleep(5000);
        break;
    }

    debug!("Shutting down remote connection...");
    mail_send.send(true);
    Ok(())
}
<|endoftext|>


<commit_before>use error::{Error,Result};
use bookoption::BookOption;

use yaml_rust::{Yaml, YamlLoader};
use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

static OPTIONS:&'static str = "
# Metadata
author:str:Anonymous                # The author of the book
title:str:Untitled                  # The title of the book
lang:str:en                         # The language of the book
subject:str                         # Subject of the book (used for EPUB metadata)
description:str                     # Description of the book (used for EPUB metadata)
cover:path                          # File name of the cover of the book
# Output options
output.epub:path                    # Output file name for EPUB rendering
output.html:path                    # Output file name for HTML rendering
output.html_dir:path                # Output directory name for HTML rendering
output.tex:path                     # Output file name for LaTeX rendering
output.pdf:path                     # Output file name for PDF rendering
output.odt:path                     # Output file name for ODT rendering

# Resources option
resources.base_path:path                 # Path where to find resources (in the source tree). By default, links and images are relative to the Markdown file. If this is set, it will be to this path.
resources.base_path.links:path           # Set base path but only for links. Useless if resources.base_path is set.
resources.base_path.images:path:.        # Set base path but only for images. Useless if resources.base_path is set.
resources.base_path.files:path:.         # Set base path but only for additional files. Useless if resources.base_path is set.
resources.out_path:path:data        # Paths where additional resources should be copied in the EPUB file or HTML directory
resources.files:str                 # Whitespace-separated list of files to embed in e.g. EPUB file

# Misc options
enable_yaml_blocks:bool:false       # Enable inline YAML blocks to override options set in config file
zip.command:str:zip                 # Command to use to zip files (for EPUB/ODT)
numbering:int:1                     # The  maximum heading levels to number (0: no numbering, 1: only chapters, ..., 6: all)
display_toc:bool:false              # If true, display a table of content in the document
toc_name:str:Table of contents      # Name of the table of contents if toc is displayed in line
autoclean:bool:true                 # Toggles cleaning of input markdown (not used for LaTeX)
verbose:bool:false                  # If set to true, print warnings in Markdown processing
side_notes:bool:false               # Display footnotes as side notes in HTML/Epub
temp_dir:path:                      # Path where to create a temporary directory (default: uses result from Rust's std::env::temp_dir())
numbering_template:str:\"{{number}}. {{title}}\" # Format of numbered titles

# HTML options
html.template:path                  # Path of an HTML template
html.css:path                       # Path of a stylesheet to use with HTML rendering
html.print_css:path                 # Path of a stylesheet to use with media print in HTML rendering
html.script:path                    # Path of a javascript file
html.display_chapter:bool:true      # Display one chapter at a time (with option to dislay all)

# Multifile HTML options
html_dir.script:path                # Path of a javascript file
html_dir.css:path                   # Path of a CSS template
html_dir.index.html:path            # Path of index.html template
html_dir.chapter.html:path          # Path of a chapter.html template

# EPUB options
epub.version:int:2                  # The EPUB version to generate
epub.css:path                       # Path of a stylesheet to use with EPUB rendering
epub.template:path                  # Path of an epub template for chapter

# LaTeX options
tex.short:bool:false                # If set to true, use article class instead of book and a the default `\\maketitle` command
tex.links_as_footnotes:bool:true    # If set to true, will add foontotes to URL of links in LaTeX/PDF output
tex.command:str:pdflatex            # LaTeX flavour to use for generating PDF
tex.template:path                   # Path of a LaTeX template file

# Deprecated options
base_path:alias:resources.base_path  # Renamed
base_path.links:alias:resources.base_path.links  # Renamed
base_path.images:alias:resources.base_path.images  # Renamed
nb_char:alias # Removed
";

/// Contains the options of a book.
#[derive(Debug)]
pub struct BookOptions {
    options: HashMap<String, BookOption>,
    deprecated: HashMap<String, Option<String>>,
    valid_bools: Vec<&'static str>,
    valid_chars: Vec<&'static str>,
    valid_strings: Vec<&'static str>,
    valid_paths: Vec<&'static str>,
    valid_ints: Vec<&'static str>,

    /// Root path of the book (unnecessary copy :/)
    pub root: PathBuf,
}

impl BookOptions {
    /// Creates a new BookOptions struct from the default compliled string
    pub fn new() -> BookOptions {
        let mut options = BookOptions {
            options: HashMap::new(),
            deprecated: HashMap::new(),
            valid_bools:vec!(),
            valid_chars:vec!(),
            valid_ints:vec!(),
            valid_strings:vec!(),
            valid_paths:vec!(),
            root: PathBuf::new(),
        };

        for (_, key, option_type, default_value) in Self::options_to_vec() {
            if key.is_none() {
                continue;
            }
            let key = key.unwrap();
            match option_type.unwrap() {
                "str" => options.valid_strings.push(key),
                "bool" => options.valid_bools.push(key),
                "int" => options.valid_ints.push(key),
                "char" => options.valid_chars.push(key),
                "path" => options.valid_paths.push(key),
                "alias" => {
                    options.deprecated.insert(key.to_owned(), default_value.map(|s| s.to_owned()));
                    continue;
                }
                _ => panic!(format!("Ill-formatted OPTIONS string: unrecognized type '{}'", option_type.unwrap())),
            }
            if key == "temp_dir" {
                // "temp_dir" has a special default value that depends on the environment
                options.set(key, &env::temp_dir().to_string_lossy()).unwrap();
                continue;
            }
            if let Some(value) = default_value {
                options.set(key, value).unwrap();
            }
        }
        options
    }

    /// Sets an option from a Yaml tuple
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, must be Yaml::String(_)
    /// * `value`: the value of the option
    ///
    /// **Returns** an error if `key` is not a String, not a valid option, of if
    /// the value is not of the right type.
    pub fn set_yaml(&mut self, key: Yaml, value: Yaml) -> Result<Option<BookOption>> {
        let key = if let Yaml::String(key) = key {
            key
        } else {
            return Err(Error::BookOption(format!("Expected a String as a key, found {:?}", key)));
        };

        if self.valid_strings.contains(&key.as_ref()) {
            // value is a string
            if let Yaml::String(value) = value {
                Ok(self.options.insert(key, BookOption::String(value)))
            } else {
                Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_paths.contains(&key.as_ref()) {
            // value is a path
            if let Yaml::String(value) = value {
                Ok(self.options.insert(key, BookOption::Path(value)))
            } else {
                Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_chars.contains(&key.as_ref()) {
            // value is a char
            if let Yaml::String(value) = value {
                let chars: Vec<_> = value.chars().collect();
                if chars.len() != 1 {
                    return Err(Error::BookOption(format!("could not parse {} as a char: does not contain exactly one char", &value)));
                }
                Ok(self.options.insert(key.to_owned(), BookOption::Char(chars[0])))
            } else {
                Err(Error::BookOption(format!("Expected a string as value containing a char for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_bools.contains(&key.as_ref()) {
            // value is a bool
            if let Yaml::Boolean(value) = value {
                Ok(self.options.insert(key, BookOption::Bool(value)))
            } else {
                Err(Error::BookOption(format!("Expected a boolean as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_ints.contains(&key.as_ref()) {
            // value is an int
            if let Yaml::Integer(value) = value {
                Ok(self.options.insert(key, BookOption::Int(value as i32)))
            } else {
                Err(Error::BookOption(format!("Expected an integer as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.deprecated.contains_key(&key) {
            let opt = self.deprecated.get(&key).unwrap().clone();
            if let Some(new_key) = opt {
                println!("{} has been deprecated, you should now use {}", &key, &new_key);
                self.set_yaml(Yaml::String(new_key), value)
            } else {
                Err(Error::BookOption(format!("key {} has been deprecated.", &key)))
            }
        } else {
            // key not recognized
            Err(Error::BookOption(format!("Unrecognized key: {}", &key)))
        }
    }

    /// Sets an option
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, e.g.: "author"
    /// * `value`: the value of the option as a string
    ///
    /// **Returns** an error either if `key` is not a valid option or if the
    /// value is not of the right type. An option containing None if key was
    /// not set, and Some(previous_value) if key was already present.
    ///
    /// # Examples
    /// ```
    /// use crowbook::Book;
    /// let mut book = Book::new(&[]);
    /// book.options.set("author", "Joan Doe").unwrap(); // ok
    /// book.options.set("numbering", "2").unwrap(); // sets numbering to chapters and subsections
    /// let result = book.options.set("autor", "John Smith");
    /// assert!(result.is_err()); // error: "author" was mispelled "autor"
    ///
    /// let result = book.options.set("numbering", "foo");
    /// assert!(result.is_err()); // error: numbering must be an int
    /// ```
    pub fn set(&mut self, key: &str, value: &str) -> Result<Option<BookOption>> {
        let result = YamlLoader::load_from_str(value);
        if let Ok(yaml_docs) = result {
            if yaml_docs.len() == 1 {
                let yaml_value = yaml_docs.into_iter().next().unwrap();
                self.set_yaml(Yaml::String(key.to_owned()), yaml_value)
            } else {
                Err(Error::BookOption(format!("value {} for key {} does not contain one and only one YAML value", value, key)))
            }
        } else {
            Err(Error::BookOption(format!("could not parse {} as a valid YAML value", value)))
        }
    }

    /// Gets an option
    pub fn get(&self, key: &str) -> Result<&BookOption> {
        self.options.get(key).ok_or_else(|| Error::InvalidOption(format!("option {} is not present", key)))
    }

    /// Gets a list of path. Only used for resources.files.
    pub fn get_paths_list(&self, key: &str) -> Result<Vec<String>> {
        if key != "resources.files" {
            return Err(Error::BookOption(format!("Can't get {} as a list of files, only valid if key is resources.files", key)));
        }

        let list = try!(try!(self.get(key))
                        .as_str())
            .split_whitespace();
        let mut res = vec!();
        for s in list {
            res.push(s.to_owned());
        }
        Ok(res)
    }

    /// Gets a string option
    pub fn get_str(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_str()
    }

    /// Get a path option
    ///
    /// Adds book's root path before it
    pub fn get_path(&self, key: &str) -> Result<String> {
        let path: &str = try!(try!(self.get(key)).as_path());
        let new_path:PathBuf = self.root.join(path);
        if let Some(path) = new_path.to_str() {
            Ok(path.to_owned())
        } else {
            Err(Error::BookOption(format!("'{}''s path contains invalid UTF-8 code", key)))
        }
    }

    /// Get a path option
    ///
    /// Don't add book's root path before it
    pub fn get_relative_path(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_path()
    }

    /// gets a bool option
    pub fn get_bool(&self, key: &str) -> Result<bool> {
        try!(self.get(key)).as_bool()
    }

    /// gets a char option
    pub fn get_char(&self, key: &str) -> Result<char> {
        try!(self.get(key)).as_char()
    }

    /// gets an int  option
    pub fn get_i32(&self, key: &str) -> Result<i32> {
        try!(self.get(key)).as_i32()
    }

    /// Returns a description of all options valid to pass to a book.
    ///
    /// # arguments
    /// * `md`: whether the output should be formatted in Markdown
    pub fn description(md: bool) -> String {
        let mut out = String::new();
        let mut previous_is_comment = true;
        for (comment, key, o_type, default) in Self::options_to_vec() {
            if key.is_none() {
                if !previous_is_comment {
                    out.push_str("\n");
                    previous_is_comment = true;
                }
                out.push_str(&format!("### {} ###\n", comment));
                continue;
            }
            previous_is_comment = false;
            let o_type = match o_type.unwrap() {
                "bool" => "boolean",
                "int" => "integer",
                "char" => "char",
                "str" => "string",
                "path" => "path",
                "alias" => "DEPRECATED",
                _ => unreachable!()
            };
            let def = if let Some(value) = default {
                value
            } else {
                "not set"
            };
            if md {
                out.push_str(&format!("- **`{}`**
    - **type**: {}
    - **default value**: `{}`
    - {}\n", key.unwrap(), o_type, def, comment));
            } else {
                out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
            }
        }
        out
    }

    /// OPTIONS to a vec of tuples (comment, key, type, default value)
    fn options_to_vec() -> Vec<(&'static str, Option<&'static str>,
                                Option<&'static str>, Option<&'static str>)> {
        let mut out = vec!();
        for line in OPTIONS.lines() {
            let line = line.trim();
            if line.is_empty() {
                continue;
            }
            if line.starts_with('#') {
                out.push((&line[1..], None, None, None));
                continue;
            }
            let v:Vec<_> = line.split('#').collect();
            let content = v[0];
            let comment = v[1];
            let v:Vec<_> = content.split(':').collect();
            let key = Some(v[0].trim());
            let option_type = Some(v[1].trim());
            let default_value = if v.len() > 2 {
                Some(v[2].trim())
            } else {
                None
            };
            out.push((comment, key, option_type, default_value));
        }
        out
    }
}
<commit_msg>Fix typo in options list<commit_after>use error::{Error,Result};
use bookoption::BookOption;

use yaml_rust::{Yaml, YamlLoader};
use std::collections::HashMap;
use std::path::PathBuf;
use std::env;

static OPTIONS:&'static str = "
# Metadata
author:str:Anonymous                # The author of the book
title:str:Untitled                  # The title of the book
lang:str:en                         # The language of the book
subject:str                         # Subject of the book (used for EPUB metadata)
description:str                     # Description of the book (used for EPUB metadata)
cover:path                          # File name of the cover of the book
# Output options
output.epub:path                    # Output file name for EPUB rendering
output.html:path                    # Output file name for HTML rendering
output.html_dir:path                # Output directory name for HTML rendering
output.tex:path                     # Output file name for LaTeX rendering
output.pdf:path                     # Output file name for PDF rendering
output.odt:path                     # Output file name for ODT rendering

# Resources option
resources.base_path:path                 # Path where to find resources (in the source tree). By default, links and images are relative to the Markdown file. If this is set, it will be to this path.
resources.base_path.links:path           # Set base path but only for links. Useless if resources.base_path is set.
resources.base_path.images:path:.        # Set base path but only for images. Useless if resources.base_path is set.
resources.base_path.files:path:.         # Set base path but only for additional files. Useless if resources.base_path is set.
resources.out_path:path:data        # Paths where additional resources should be copied in the EPUB file or HTML directory
resources.files:str                 # Whitespace-separated list of files to embed in e.g. EPUB file

# Misc options
enable_yaml_blocks:bool:false       # Enable inline YAML blocks to override options set in config file
zip.command:str:zip                 # Command to use to zip files (for EPUB/ODT)
numbering:int:1                     # The  maximum heading levels to number (0: no numbering, 1: only chapters, ..., 6: all)
display_toc:bool:false              # If true, display a table of content in the document
toc_name:str:Table of contents      # Name of the table of contents if toc is displayed in line
autoclean:bool:true                 # Toggles cleaning of input markdown (not used for LaTeX)
verbose:bool:false                  # If set to true, print warnings in Markdown processing
side_notes:bool:false               # Display footnotes as side notes in HTML/Epub
temp_dir:path:                      # Path where to create a temporary directory (default: uses result from Rust's std::env::temp_dir())
numbering_template:str:\"{{number}}. {{title}}\" # Format of numbered titles

# HTML options
html.template:path                  # Path of an HTML template
html.css:path                       # Path of a stylesheet to use with HTML rendering
html.print_css:path                 # Path of a stylesheet to use with media print in HTML rendering
html.script:path                    # Path of a javascript file
html.display_chapter:bool:true      # Display one chapter at a time (with option to display all)

# Multifile HTML options
html_dir.script:path                # Path of a javascript file
html_dir.css:path                   # Path of a CSS template
html_dir.index.html:path            # Path of index.html template
html_dir.chapter.html:path          # Path of a chapter.html template

# EPUB options
epub.version:int:2                  # The EPUB version to generate
epub.css:path                       # Path of a stylesheet to use with EPUB rendering
epub.template:path                  # Path of an epub template for chapter

# LaTeX options
tex.short:bool:false                # If set to true, use article class instead of book and a the default `\\maketitle` command
tex.links_as_footnotes:bool:true    # If set to true, will add foontotes to URL of links in LaTeX/PDF output
tex.command:str:pdflatex            # LaTeX flavour to use for generating PDF
tex.template:path                   # Path of a LaTeX template file

# Deprecated options
base_path:alias:resources.base_path  # Renamed
base_path.links:alias:resources.base_path.links  # Renamed
base_path.images:alias:resources.base_path.images  # Renamed
nb_char:alias # Removed
";

/// Contains the options of a book.
#[derive(Debug)]
pub struct BookOptions {
    options: HashMap<String, BookOption>,
    deprecated: HashMap<String, Option<String>>,
    valid_bools: Vec<&'static str>,
    valid_chars: Vec<&'static str>,
    valid_strings: Vec<&'static str>,
    valid_paths: Vec<&'static str>,
    valid_ints: Vec<&'static str>,

    /// Root path of the book (unnecessary copy :/)
    pub root: PathBuf,
}

impl BookOptions {
    /// Creates a new BookOptions struct from the default compliled string
    pub fn new() -> BookOptions {
        let mut options = BookOptions {
            options: HashMap::new(),
            deprecated: HashMap::new(),
            valid_bools:vec!(),
            valid_chars:vec!(),
            valid_ints:vec!(),
            valid_strings:vec!(),
            valid_paths:vec!(),
            root: PathBuf::new(),
        };

        for (_, key, option_type, default_value) in Self::options_to_vec() {
            if key.is_none() {
                continue;
            }
            let key = key.unwrap();
            match option_type.unwrap() {
                "str" => options.valid_strings.push(key),
                "bool" => options.valid_bools.push(key),
                "int" => options.valid_ints.push(key),
                "char" => options.valid_chars.push(key),
                "path" => options.valid_paths.push(key),
                "alias" => {
                    options.deprecated.insert(key.to_owned(), default_value.map(|s| s.to_owned()));
                    continue;
                }
                _ => panic!(format!("Ill-formatted OPTIONS string: unrecognized type '{}'", option_type.unwrap())),
            }
            if key == "temp_dir" {
                // "temp_dir" has a special default value that depends on the environment
                options.set(key, &env::temp_dir().to_string_lossy()).unwrap();
                continue;
            }
            if let Some(value) = default_value {
                options.set(key, value).unwrap();
            }
        }
        options
    }

    /// Sets an option from a Yaml tuple
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, must be Yaml::String(_)
    /// * `value`: the value of the option
    ///
    /// **Returns** an error if `key` is not a String, not a valid option, of if
    /// the value is not of the right type.
    pub fn set_yaml(&mut self, key: Yaml, value: Yaml) -> Result<Option<BookOption>> {
        let key = if let Yaml::String(key) = key {
            key
        } else {
            return Err(Error::BookOption(format!("Expected a String as a key, found {:?}", key)));
        };

        if self.valid_strings.contains(&key.as_ref()) {
            // value is a string
            if let Yaml::String(value) = value {
                Ok(self.options.insert(key, BookOption::String(value)))
            } else {
                Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_paths.contains(&key.as_ref()) {
            // value is a path
            if let Yaml::String(value) = value {
                Ok(self.options.insert(key, BookOption::Path(value)))
            } else {
                Err(Error::BookOption(format!("Expected a string as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_chars.contains(&key.as_ref()) {
            // value is a char
            if let Yaml::String(value) = value {
                let chars: Vec<_> = value.chars().collect();
                if chars.len() != 1 {
                    return Err(Error::BookOption(format!("could not parse {} as a char: does not contain exactly one char", &value)));
                }
                Ok(self.options.insert(key.to_owned(), BookOption::Char(chars[0])))
            } else {
                Err(Error::BookOption(format!("Expected a string as value containing a char for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_bools.contains(&key.as_ref()) {
            // value is a bool
            if let Yaml::Boolean(value) = value {
                Ok(self.options.insert(key, BookOption::Bool(value)))
            } else {
                Err(Error::BookOption(format!("Expected a boolean as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.valid_ints.contains(&key.as_ref()) {
            // value is an int
            if let Yaml::Integer(value) = value {
                Ok(self.options.insert(key, BookOption::Int(value as i32)))
            } else {
                Err(Error::BookOption(format!("Expected an integer as value for key {}, found {:?}", &key, &value)))
            }
        } else if self.deprecated.contains_key(&key) {
            let opt = self.deprecated.get(&key).unwrap().clone();
            if let Some(new_key) = opt {
                println!("{} has been deprecated, you should now use {}", &key, &new_key);
                self.set_yaml(Yaml::String(new_key), value)
            } else {
                Err(Error::BookOption(format!("key {} has been deprecated.", &key)))
            }
        } else {
            // key not recognized
            Err(Error::BookOption(format!("Unrecognized key: {}", &key)))
        }
    }

    /// Sets an option
    ///
    /// # Arguments
    /// * `key`: the identifier of the option, e.g.: "author"
    /// * `value`: the value of the option as a string
    ///
    /// **Returns** an error either if `key` is not a valid option or if the
    /// value is not of the right type. An option containing None if key was
    /// not set, and Some(previous_value) if key was already present.
    ///
    /// # Examples
    /// ```
    /// use crowbook::Book;
    /// let mut book = Book::new(&[]);
    /// book.options.set("author", "Joan Doe").unwrap(); // ok
    /// book.options.set("numbering", "2").unwrap(); // sets numbering to chapters and subsections
    /// let result = book.options.set("autor", "John Smith");
    /// assert!(result.is_err()); // error: "author" was mispelled "autor"
    ///
    /// let result = book.options.set("numbering", "foo");
    /// assert!(result.is_err()); // error: numbering must be an int
    /// ```
    pub fn set(&mut self, key: &str, value: &str) -> Result<Option<BookOption>> {
        let result = YamlLoader::load_from_str(value);
        if let Ok(yaml_docs) = result {
            if yaml_docs.len() == 1 {
                let yaml_value = yaml_docs.into_iter().next().unwrap();
                self.set_yaml(Yaml::String(key.to_owned()), yaml_value)
            } else {
                Err(Error::BookOption(format!("value {} for key {} does not contain one and only one YAML value", value, key)))
            }
        } else {
            Err(Error::BookOption(format!("could not parse {} as a valid YAML value", value)))
        }
    }

    /// Gets an option
    pub fn get(&self, key: &str) -> Result<&BookOption> {
        self.options.get(key).ok_or_else(|| Error::InvalidOption(format!("option {} is not present", key)))
    }

    /// Gets a list of path. Only used for resources.files.
    pub fn get_paths_list(&self, key: &str) -> Result<Vec<String>> {
        if key != "resources.files" {
            return Err(Error::BookOption(format!("Can't get {} as a list of files, only valid if key is resources.files", key)));
        }

        let list = try!(try!(self.get(key))
                        .as_str())
            .split_whitespace();
        let mut res = vec!();
        for s in list {
            res.push(s.to_owned());
        }
        Ok(res)
    }

    /// Gets a string option
    pub fn get_str(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_str()
    }

    /// Get a path option
    ///
    /// Adds book's root path before it
    pub fn get_path(&self, key: &str) -> Result<String> {
        let path: &str = try!(try!(self.get(key)).as_path());
        let new_path:PathBuf = self.root.join(path);
        if let Some(path) = new_path.to_str() {
            Ok(path.to_owned())
        } else {
            Err(Error::BookOption(format!("'{}''s path contains invalid UTF-8 code", key)))
        }
    }

    /// Get a path option
    ///
    /// Don't add book's root path before it
    pub fn get_relative_path(&self, key: &str) -> Result<&str> {
        try!(self.get(key)).as_path()
    }

    /// gets a bool option
    pub fn get_bool(&self, key: &str) -> Result<bool> {
        try!(self.get(key)).as_bool()
    }

    /// gets a char option
    pub fn get_char(&self, key: &str) -> Result<char> {
        try!(self.get(key)).as_char()
    }

    /// gets an int  option
    pub fn get_i32(&self, key: &str) -> Result<i32> {
        try!(self.get(key)).as_i32()
    }

    /// Returns a description of all options valid to pass to a book.
    ///
    /// # arguments
    /// * `md`: whether the output should be formatted in Markdown
    pub fn description(md: bool) -> String {
        let mut out = String::new();
        let mut previous_is_comment = true;
        for (comment, key, o_type, default) in Self::options_to_vec() {
            if key.is_none() {
                if !previous_is_comment {
                    out.push_str("\n");
                    previous_is_comment = true;
                }
                out.push_str(&format!("### {} ###\n", comment));
                continue;
            }
            previous_is_comment = false;
            let o_type = match o_type.unwrap() {
                "bool" => "boolean",
                "int" => "integer",
                "char" => "char",
                "str" => "string",
                "path" => "path",
                "alias" => "DEPRECATED",
                _ => unreachable!()
            };
            let def = if let Some(value) = default {
                value
            } else {
                "not set"
            };
            if md {
                out.push_str(&format!("- **`{}`**
    - **type**: {}
    - **default value**: `{}`
    - {}\n", key.unwrap(), o_type, def, comment));
            } else {
                out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
            }
        }
        out
    }

    /// OPTIONS to a vec of tuples (comment, key, type, default value)
    fn options_to_vec() -> Vec<(&'static str, Option<&'static str>,
                                Option<&'static str>, Option<&'static str>)> {
        let mut out = vec!();
        for line in OPTIONS.lines() {
            let line = line.trim();
            if line.is_empty() {
                continue;
            }
            if line.starts_with('#') {
                out.push((&line[1..], None, None, None));
                continue;
            }
            let v:Vec<_> = line.split('#').collect();
            let content = v[0];
            let comment = v[1];
            let v:Vec<_> = content.split(':').collect();
            let key = Some(v[0].trim());
            let option_type = Some(v[1].trim());
            let default_value = if v.len() > 2 {
                Some(v[2].trim())
            } else {
                None
            };
            out.push((comment, key, option_type, default_value));
        }
        out
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Integer and floating-point number formatting

// FIXME: #6220 Implement floating point formatting

#![allow(unsigned_negation)]

use prelude::*;

use fmt;
use num::Zero;
use ops::{Div, Rem, Sub};
use str;

#[doc(hidden)]
trait Int: Zero + PartialEq + PartialOrd + Div<Output=Self> + Rem<Output=Self> +
           Sub<Output=Self> + Copy {
    fn from_u8(u: u8) -> Self;
    fn to_u8(&self) -> u8;
}

macro_rules! doit {
    ($($t:ident)*) => ($(impl Int for $t {
        fn from_u8(u: u8) -> $t { u as $t }
        fn to_u8(&self) -> u8 { *self as u8 }
    })*)
}
doit! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }

/// A type that represents a specific radix
#[doc(hidden)]
trait GenericRadix {
    /// The number of digits.
    fn base(&self) -> u8;

    /// A radix-specific prefix string.
    fn prefix(&self) -> &'static str { "" }

    /// Converts an integer to corresponding radix digit.
    fn digit(&self, x: u8) -> u8;

    /// Format an integer using the radix using a formatter.
    fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
        // The radix can be as low as 2, so we need a buffer of at least 64
        // characters for a base 2 number.
        let zero = T::zero();
        let is_positive = x >= zero;
        let mut buf = [0; 64];
        let mut curr = buf.len();
        let base = T::from_u8(self.base());
        if is_positive {
            // Accumulate each digit of the number from the least significant
            // to the most significant figure.
            for byte in buf.iter_mut().rev() {
                let n = x % base;              // Get the current place value.
                x = x / base;                  // Deaccumulate the number.
                *byte = self.digit(n.to_u8()); // Store the digit in the buffer.
                curr -= 1;
                if x == zero { break };        // No more digits left to accumulate.
            }
        } else {
            // Do the same as above, but accounting for two's complement.
            for byte in buf.iter_mut().rev() {
                let n = zero - (x % base);     // Get the current place value.
                x = x / base;                  // Deaccumulate the number.
                *byte = self.digit(n.to_u8()); // Store the digit in the buffer.
                curr -= 1;
                if x == zero { break };        // No more digits left to accumulate.
            }
        }
        let buf = unsafe { str::from_utf8_unchecked(&buf[curr..]) };
        f.pad_integral(is_positive, self.prefix(), buf)
    }
}

/// A binary (base 2) radix
#[derive(Clone, PartialEq)]
struct Binary;

/// An octal (base 8) radix
#[derive(Clone, PartialEq)]
struct Octal;

/// A decimal (base 10) radix
#[derive(Clone, PartialEq)]
struct Decimal;

/// A hexadecimal (base 16) radix, formatted with lower-case characters
#[derive(Clone, PartialEq)]
struct LowerHex;

/// A hexadecimal (base 16) radix, formatted with upper-case characters
#[derive(Clone, PartialEq)]
struct UpperHex;

macro_rules! radix {
    ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
        impl GenericRadix for $T {
            fn base(&self) -> u8 { $base }
            fn prefix(&self) -> &'static str { $prefix }
            fn digit(&self, x: u8) -> u8 {
                match x {
                    $($x => $conv,)+
                    x => panic!("number not in the range 0..{}: {}", self.base() - 1, x),
                }
            }
        }
    }
}

radix! { Binary,    2, "0b", x @  0 ...  2 => b'0' + x }
radix! { Octal,     8, "0o", x @  0 ...  7 => b'0' + x }
radix! { Decimal,  10, "",   x @  0 ...  9 => b'0' + x }
radix! { LowerHex, 16, "0x", x @  0 ...  9 => b'0' + x,
                             x @ 10 ... 15 => b'a' + (x - 10) }
radix! { UpperHex, 16, "0x", x @  0 ...  9 => b'0' + x,
                             x @ 10 ... 15 => b'A' + (x - 10) }

/// A radix with in the range of `2..36`.
#[derive(Clone, Copy, PartialEq)]
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
pub struct Radix {
    base: u8,
}

impl Radix {
    fn new(base: u8) -> Radix {
        assert!(2 <= base && base <= 36, "the base must be in the range of 2..36: {}", base);
        Radix { base: base }
    }
}

impl GenericRadix for Radix {
    fn base(&self) -> u8 { self.base }
    fn digit(&self, x: u8) -> u8 {
        match x {
            x @  0 ... 9 => b'0' + x,
            x if x < self.base() => b'a' + (x - 10),
            x => panic!("number not in the range 0..{}: {}", self.base() - 1, x),
        }
    }
}

/// A helper type for formatting radixes.
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
#[derive(Copy, Clone)]
pub struct RadixFmt<T, R>(T, R);

/// Constructs a radix formatter in the range of `2..36`.
///
/// # Examples
///
/// ```
/// # #![feature(fmt_radix)]
/// use std::fmt::radix;
/// assert_eq!(format!("{}", radix(55, 36)), "1j".to_string());
/// ```
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
    RadixFmt(x, Radix::new(base))
}

macro_rules! radix_fmt {
    ($T:ty as $U:ty, $fmt:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Debug for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Display::fmt(self, f)
            }
        }
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Display for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) }
            }
        }
    }
}
macro_rules! int_base {
    ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::$Trait for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                $Radix.fmt_int(*self as $U, f)
            }
        }
    }
}

macro_rules! debug {
    ($T:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Debug for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Display::fmt(self, f)
            }
        }
    }
}
macro_rules! integer {
    ($Int:ident, $Uint:ident) => {
        int_base! { Display  for $Int as $Int   -> Decimal }
        int_base! { Binary   for $Int as $Uint  -> Binary }
        int_base! { Octal    for $Int as $Uint  -> Octal }
        int_base! { LowerHex for $Int as $Uint  -> LowerHex }
        int_base! { UpperHex for $Int as $Uint  -> UpperHex }
        radix_fmt! { $Int as $Int, fmt_int }
        debug! { $Int }

        int_base! { Display  for $Uint as $Uint -> Decimal }
        int_base! { Binary   for $Uint as $Uint -> Binary }
        int_base! { Octal    for $Uint as $Uint -> Octal }
        int_base! { LowerHex for $Uint as $Uint -> LowerHex }
        int_base! { UpperHex for $Uint as $Uint -> UpperHex }
        radix_fmt! { $Uint as $Uint, fmt_int }
        debug! { $Uint }
    }
}
integer! { isize, usize }
integer! { i8, u8 }
integer! { i16, u16 }
integer! { i32, u32 }
integer! { i64, u64 }
<commit_msg>optimize integer formatting<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Integer and floating-point number formatting

// FIXME: #6220 Implement floating point formatting

#![allow(unsigned_negation)]

use prelude::*;

use fmt;
use num::Zero;
use ops::{Div, Rem, Sub};
use str;
use slice;
use ptr;
use mem;

#[doc(hidden)]
trait Int: Zero + PartialEq + PartialOrd + Div<Output=Self> + Rem<Output=Self> +
           Sub<Output=Self> + Copy {
    fn from_u8(u: u8) -> Self;
    fn to_u8(&self) -> u8;
    fn to_u32(&self) -> u32;
    fn to_u64(&self) -> u64;
}

macro_rules! doit {
    ($($t:ident)*) => ($(impl Int for $t {
        fn from_u8(u: u8) -> $t { u as $t }
        fn to_u8(&self) -> u8 { *self as u8 }
        fn to_u32(&self) -> u32 { *self as u32 }
        fn to_u64(&self) -> u64 { *self as u64 }
    })*)
}
doit! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }

/// A type that represents a specific radix
#[doc(hidden)]
trait GenericRadix {
    /// The number of digits.
    fn base(&self) -> u8;

    /// A radix-specific prefix string.
    fn prefix(&self) -> &'static str { "" }

    /// Converts an integer to corresponding radix digit.
    fn digit(&self, x: u8) -> u8;

    /// Format an integer using the radix using a formatter.
    fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
        // The radix can be as low as 2, so we need a buffer of at least 64
        // characters for a base 2 number.
        let zero = T::zero();
        let is_positive = x >= zero;
        let mut buf = [0; 64];
        let mut curr = buf.len();
        let base = T::from_u8(self.base());
        if is_positive {
            // Accumulate each digit of the number from the least significant
            // to the most significant figure.
            for byte in buf.iter_mut().rev() {
                let n = x % base;              // Get the current place value.
                x = x / base;                  // Deaccumulate the number.
                *byte = self.digit(n.to_u8()); // Store the digit in the buffer.
                curr -= 1;
                if x == zero { break };        // No more digits left to accumulate.
            }
        } else {
            // Do the same as above, but accounting for two's complement.
            for byte in buf.iter_mut().rev() {
                let n = zero - (x % base);     // Get the current place value.
                x = x / base;                  // Deaccumulate the number.
                *byte = self.digit(n.to_u8()); // Store the digit in the buffer.
                curr -= 1;
                if x == zero { break };        // No more digits left to accumulate.
            }
        }
        let buf = unsafe { str::from_utf8_unchecked(&buf[curr..]) };
        f.pad_integral(is_positive, self.prefix(), buf)
    }
}

/// A binary (base 2) radix
#[derive(Clone, PartialEq)]
struct Binary;

/// An octal (base 8) radix
#[derive(Clone, PartialEq)]
struct Octal;

/// A decimal (base 10) radix
#[derive(Clone, PartialEq)]
struct Decimal;

/// A hexadecimal (base 16) radix, formatted with lower-case characters
#[derive(Clone, PartialEq)]
struct LowerHex;

/// A hexadecimal (base 16) radix, formatted with upper-case characters
#[derive(Clone, PartialEq)]
struct UpperHex;

macro_rules! radix {
    ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
        impl GenericRadix for $T {
            fn base(&self) -> u8 { $base }
            fn prefix(&self) -> &'static str { $prefix }
            fn digit(&self, x: u8) -> u8 {
                match x {
                    $($x => $conv,)+
                    x => panic!("number not in the range 0..{}: {}", self.base() - 1, x),
                }
            }
        }
    }
}

radix! { Binary,    2, "0b", x @  0 ...  2 => b'0' + x }
radix! { Octal,     8, "0o", x @  0 ...  7 => b'0' + x }
radix! { Decimal,  10, "",   x @  0 ...  9 => b'0' + x }
radix! { LowerHex, 16, "0x", x @  0 ...  9 => b'0' + x,
                             x @ 10 ... 15 => b'a' + (x - 10) }
radix! { UpperHex, 16, "0x", x @  0 ...  9 => b'0' + x,
                             x @ 10 ... 15 => b'A' + (x - 10) }

/// A radix with in the range of `2..36`.
#[derive(Clone, Copy, PartialEq)]
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
pub struct Radix {
    base: u8,
}

impl Radix {
    fn new(base: u8) -> Radix {
        assert!(2 <= base && base <= 36, "the base must be in the range of 2..36: {}", base);
        Radix { base: base }
    }
}

impl GenericRadix for Radix {
    fn base(&self) -> u8 { self.base }
    fn digit(&self, x: u8) -> u8 {
        match x {
            x @  0 ... 9 => b'0' + x,
            x if x < self.base() => b'a' + (x - 10),
            x => panic!("number not in the range 0..{}: {}", self.base() - 1, x),
        }
    }
}

/// A helper type for formatting radixes.
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
#[derive(Copy, Clone)]
pub struct RadixFmt<T, R>(T, R);

/// Constructs a radix formatter in the range of `2..36`.
///
/// # Examples
///
/// ```
/// # #![feature(fmt_radix)]
/// use std::fmt::radix;
/// assert_eq!(format!("{}", radix(55, 36)), "1j".to_string());
/// ```
#[unstable(feature = "fmt_radix",
           reason = "may be renamed or move to a different module")]
pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
    RadixFmt(x, Radix::new(base))
}

macro_rules! radix_fmt {
    ($T:ty as $U:ty, $fmt:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Debug for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Display::fmt(self, f)
            }
        }
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Display for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) }
            }
        }
    }
}

macro_rules! int_base {
    ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::$Trait for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                $Radix.fmt_int(*self as $U, f)
            }
        }
    }
}

macro_rules! debug {
    ($T:ident) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl fmt::Debug for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Display::fmt(self, f)
            }
        }
    }
}

macro_rules! integer {
    ($Int:ident, $Uint:ident) => {
        int_base! { Binary   for $Int as $Uint  -> Binary }
        int_base! { Octal    for $Int as $Uint  -> Octal }
        int_base! { LowerHex for $Int as $Uint  -> LowerHex }
        int_base! { UpperHex for $Int as $Uint  -> UpperHex }
        radix_fmt! { $Int as $Int, fmt_int }
        debug! { $Int }

        int_base! { Binary   for $Uint as $Uint -> Binary }
        int_base! { Octal    for $Uint as $Uint -> Octal }
        int_base! { LowerHex for $Uint as $Uint -> LowerHex }
        int_base! { UpperHex for $Uint as $Uint -> UpperHex }
        radix_fmt! { $Uint as $Uint, fmt_int }
        debug! { $Uint }
    }
}
integer! { isize, usize }
integer! { i8, u8 }
integer! { i16, u16 }
integer! { i32, u32 }
integer! { i64, u64 }

const DEC_DIGITS_LUT: &'static[u8] =
    b"0001020304050607080910111213141516171819\
      2021222324252627282930313233343536373839\
      4041424344454647484950515253545556575859\
      6061626364656667686970717273747576777879\
      8081828384858687888990919293949596979899";

macro_rules! impl_Display {
    ($($t:ident),*: $conv_fn:ident) => ($(
    impl fmt::Display for $t {
        #[allow(unused_comparisons)]
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let is_positive = *self >= 0;
            let mut n = if is_positive {
                self.$conv_fn()
            } else {
                // convert the negative num to positive by summing 1 to it's 2 complement
                (!self.$conv_fn()).wrapping_add(1)
            };
            let mut buf: [u8; 20] = unsafe { mem::uninitialized() };
            let mut curr = buf.len() as isize;
            let buf_ptr = buf.as_mut_ptr();
            let lut_ptr = DEC_DIGITS_LUT.as_ptr();

            unsafe {
                // eagerly decode 4 characters at a time
                if <$t>::max_value() as u64 >= 10000 {
                    while n >= 10000 {
                        let rem = (n % 10000) as isize;
                        n /= 10000;

                        let d1 = (rem / 100) << 1;
                        let d2 = (rem % 100) << 1;
                        curr -= 4;
                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
                        ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
                    }
                }

                // if we reach here numbers are <= 9999, so at most 4 chars long
                let mut n = n as isize; // possibly reduce 64bit math

                // decode 2 more chars, if > 2 chars
                if n >= 100 {
                    let d1 = (n % 100) << 1;
                    n /= 100;
                    curr -= 2;
                    ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
                }

                // decode last 1 or 2 chars
                if n < 10 {
                    curr -= 1;
                    *buf_ptr.offset(curr) = (n as u8) + 48;
                } else {
                    let d1 = n << 1;
                    curr -= 2;
                    ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
                }
            }

            let buf_slice = unsafe {
                str::from_utf8_unchecked(
                    slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
            };
            f.pad_integral(is_positive, "", buf_slice)
        }
    })*);
}

impl_Display!(i8, u8, i16, u16, i32, u32: to_u32);
impl_Display!(i64, u64: to_u64);
#[cfg(target_pointer_width = "32")]
impl_Display!(isize, usize: to_u32);
#[cfg(target_pointer_width = "64")]
impl_Display!(isize, usize: to_u64);
<|endoftext|>


<commit_before>use crate::clean::*;

crate fn strip_item(mut item: Item) -> Item {
    if !matches!(*item.kind, StrippedItem(..)) {
        item.kind = box StrippedItem(item.kind);
    }
    item
}

crate trait DocFolder: Sized {
    fn fold_item(&mut self, item: Item) -> Option<Item> {
        Some(self.fold_item_recur(item))
    }

    /// don't override!
    fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
        match kind {
            StrippedItem(..) => unreachable!(),
            ModuleItem(i) => ModuleItem(self.fold_mod(i)),
            StructItem(mut i) => {
                let num_fields = i.fields.len();
                i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.fields_stripped |=
                    num_fields != i.fields.len() || i.fields.iter().any(|f| f.is_stripped());
                StructItem(i)
            }
            UnionItem(mut i) => {
                let num_fields = i.fields.len();
                i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.fields_stripped |=
                    num_fields != i.fields.len() || i.fields.iter().any(|f| f.is_stripped());
                UnionItem(i)
            }
            EnumItem(mut i) => {
                let num_variants = i.variants.len();
                i.variants = i.variants.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.variants_stripped |=
                    num_variants != i.variants.len() || i.variants.iter().any(|f| f.is_stripped());
                EnumItem(i)
            }
            TraitItem(mut i) => {
                i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
                TraitItem(i)
            }
            ImplItem(mut i) => {
                i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
                ImplItem(i)
            }
            VariantItem(i) => {
                let i2 = i.clone(); // this clone is small
                match i {
                    Variant::Struct(mut j) => {
                        let num_fields = j.fields.len();
                        j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                        j.fields_stripped |= num_fields != j.fields.len()
                            || j.fields.iter().any(|f| f.is_stripped());
                        VariantItem(Variant::Struct(j))
                    }
                    Variant::Tuple(fields) => {
                        let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                        VariantItem(Variant::Tuple(fields))
                    }
                    _ => VariantItem(i2),
                }
            }
            x => x,
        }
    }

    /// don't override!
    fn fold_item_recur(&mut self, mut item: Item) -> Item {
        item.kind = box match *item.kind {
            StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)),
            _ => self.fold_inner_recur(*item.kind),
        };
        item
    }

    fn fold_mod(&mut self, m: Module) -> Module {
        Module {
            span: m.span,
            items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect(),
        }
    }

    fn fold_crate(&mut self, mut c: Crate) -> Crate {
        c.module = self.fold_item(c.module).unwrap();

        {
            let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
            for (k, mut v) in external_traits {
                v.trait_.items =
                    v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
                c.external_traits.borrow_mut().insert(k, v);
            }
        }
        c
    }
}
<commit_msg>rustdoc: Remove unnecessary clone in `DocFolder`<commit_after>use crate::clean::*;

crate fn strip_item(mut item: Item) -> Item {
    if !matches!(*item.kind, StrippedItem(..)) {
        item.kind = box StrippedItem(item.kind);
    }
    item
}

crate trait DocFolder: Sized {
    fn fold_item(&mut self, item: Item) -> Option<Item> {
        Some(self.fold_item_recur(item))
    }

    /// don't override!
    fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
        match kind {
            StrippedItem(..) => unreachable!(),
            ModuleItem(i) => ModuleItem(self.fold_mod(i)),
            StructItem(mut i) => {
                let num_fields = i.fields.len();
                i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.fields_stripped |=
                    num_fields != i.fields.len() || i.fields.iter().any(|f| f.is_stripped());
                StructItem(i)
            }
            UnionItem(mut i) => {
                let num_fields = i.fields.len();
                i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.fields_stripped |=
                    num_fields != i.fields.len() || i.fields.iter().any(|f| f.is_stripped());
                UnionItem(i)
            }
            EnumItem(mut i) => {
                let num_variants = i.variants.len();
                i.variants = i.variants.into_iter().filter_map(|x| self.fold_item(x)).collect();
                i.variants_stripped |=
                    num_variants != i.variants.len() || i.variants.iter().any(|f| f.is_stripped());
                EnumItem(i)
            }
            TraitItem(mut i) => {
                i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
                TraitItem(i)
            }
            ImplItem(mut i) => {
                i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
                ImplItem(i)
            }
            VariantItem(i) => {
                match i {
                    Variant::Struct(mut j) => {
                        let num_fields = j.fields.len();
                        j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                        j.fields_stripped |= num_fields != j.fields.len()
                            || j.fields.iter().any(|f| f.is_stripped());
                        VariantItem(Variant::Struct(j))
                    }
                    Variant::Tuple(fields) => {
                        let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
                        VariantItem(Variant::Tuple(fields))
                    }
                    i => VariantItem(i),
                }
            }
            x => x,
        }
    }

    /// don't override!
    fn fold_item_recur(&mut self, mut item: Item) -> Item {
        item.kind = box match *item.kind {
            StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)),
            _ => self.fold_inner_recur(*item.kind),
        };
        item
    }

    fn fold_mod(&mut self, m: Module) -> Module {
        Module {
            span: m.span,
            items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect(),
        }
    }

    fn fold_crate(&mut self, mut c: Crate) -> Crate {
        c.module = self.fold_item(c.module).unwrap();

        {
            let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
            for (k, mut v) in external_traits {
                v.trait_.items =
                    v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
                c.external_traits.borrow_mut().insert(k, v);
            }
        }
        c
    }
}
<|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/. */

//! Tracking of pending loads in a document.
//! https://html.spec.whatwg.org/multipage/#the-end

use dom::bindings::js::JS;
use dom::document::Document;
use ipc_channel::ipc::IpcSender;
use net_traits::{CoreResourceMsg, FetchResponseMsg, ResourceThreads, IpcSend};
use net_traits::request::RequestInit;
use servo_url::ServoUrl;
use std::thread;

#[derive(Clone, Debug, HeapSizeOf, JSTraceable, PartialEq)]
pub enum LoadType {
    Image(ServoUrl),
    Script(ServoUrl),
    Subframe(ServoUrl),
    Stylesheet(ServoUrl),
    PageSource(ServoUrl),
    Media(ServoUrl),
}

impl LoadType {
    fn url(&self) -> &ServoUrl {
        match *self {
            LoadType::Image(ref url) |
            LoadType::Script(ref url) |
            LoadType::Subframe(ref url) |
            LoadType::Stylesheet(ref url) |
            LoadType::Media(ref url) |
            LoadType::PageSource(ref url) => url,
        }
    }
}

/// Canary value ensuring that manually added blocking loads (ie. ones that weren't
/// created via DocumentLoader::fetch_async) are always removed by the time
/// that the owner is destroyed.
#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub struct LoadBlocker {
    /// The document whose load event is blocked by this object existing.
    doc: JS<Document>,
    /// The load that is blocking the document's load event.
    load: Option<LoadType>,
}

impl LoadBlocker {
    /// Mark the document's load event as blocked on this new load.
    pub fn new(doc: &Document, load: LoadType) -> LoadBlocker {
        doc.mut_loader().add_blocking_load(load.clone());
        LoadBlocker {
            doc: JS::from_ref(doc),
            load: Some(load),
        }
    }

    /// Remove this load from the associated document's list of blocking loads.
    pub fn terminate(blocker: &mut Option<LoadBlocker>) {
        if let Some(this) = blocker.as_mut() {
            this.doc.finish_load(this.load.take().unwrap());
        }
        *blocker = None;
    }

    /// Return the url associated with this load.
    pub fn url(&self) -> Option<&ServoUrl> {
        self.load.as_ref().map(LoadType::url)
    }
}

impl Drop for LoadBlocker {
    fn drop(&mut self) {
        if !thread::panicking() {
            debug_assert!(self.load.is_none());
        }
    }
}

#[derive(HeapSizeOf, JSTraceable)]
pub struct DocumentLoader {
    resource_threads: ResourceThreads,
    blocking_loads: Vec<LoadType>,
    events_inhibited: bool,
}

impl DocumentLoader {
    pub fn new(existing: &DocumentLoader) -> DocumentLoader {
        DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
    }

    pub fn new_with_threads(resource_threads: ResourceThreads,
                            initial_load: Option<ServoUrl>) -> DocumentLoader {
        debug!("Initial blocking load {:?}.", initial_load);
        let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect();

        DocumentLoader {
            resource_threads: resource_threads,
            blocking_loads: initial_loads,
            events_inhibited: false,
        }
    }

    /// Add a load to the list of blocking loads.
    fn add_blocking_load(&mut self, load: LoadType) {
        debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len());
        self.blocking_loads.push(load);
    }

    /// Initiate a new fetch.
    pub fn fetch_async(&mut self,
                       load: LoadType,
                       request: RequestInit,
                       fetch_target: IpcSender<FetchResponseMsg>) {
        self.add_blocking_load(load);
        self.fetch_async_background(request, fetch_target);
    }

    /// Initiate a new fetch that does not block the document load event.
    pub fn fetch_async_background(&self,
                                  request: RequestInit,
                                  fetch_target: IpcSender<FetchResponseMsg>) {
        self.resource_threads.sender().send(CoreResourceMsg::Fetch(request, fetch_target)).unwrap();
    }

    /// Mark an in-progress network request complete.
    pub fn finish_load(&mut self, load: &LoadType) {
        debug!("Removing blocking load {:?} ({}).", load, self.blocking_loads.len());
        let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == *load);
        self.blocking_loads.remove(idx.expect(&format!("unknown completed load {:?}", load)));
    }

    pub fn is_blocked(&self) -> bool {
        // TODO: Ensure that we report blocked if parsing is still ongoing.
        !self.blocking_loads.is_empty()
    }

    pub fn inhibit_events(&mut self) {
        self.events_inhibited = true;
    }

    pub fn events_inhibited(&self) -> bool {
        self.events_inhibited
    }

    pub fn resource_threads(&self) -> &ResourceThreads {
        &self.resource_threads
    }
}
<commit_msg>Auto merge of #18603 - tigercosmos:allocating, r=KiChjang<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/. */

//! Tracking of pending loads in a document.
//! https://html.spec.whatwg.org/multipage/#the-end

use dom::bindings::js::JS;
use dom::document::Document;
use ipc_channel::ipc::IpcSender;
use net_traits::{CoreResourceMsg, FetchResponseMsg, ResourceThreads, IpcSend};
use net_traits::request::RequestInit;
use servo_url::ServoUrl;
use std::thread;

#[derive(Clone, Debug, HeapSizeOf, JSTraceable, PartialEq)]
pub enum LoadType {
    Image(ServoUrl),
    Script(ServoUrl),
    Subframe(ServoUrl),
    Stylesheet(ServoUrl),
    PageSource(ServoUrl),
    Media(ServoUrl),
}

impl LoadType {
    fn url(&self) -> &ServoUrl {
        match *self {
            LoadType::Image(ref url) |
            LoadType::Script(ref url) |
            LoadType::Subframe(ref url) |
            LoadType::Stylesheet(ref url) |
            LoadType::Media(ref url) |
            LoadType::PageSource(ref url) => url,
        }
    }
}

/// Canary value ensuring that manually added blocking loads (ie. ones that weren't
/// created via DocumentLoader::fetch_async) are always removed by the time
/// that the owner is destroyed.
#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub struct LoadBlocker {
    /// The document whose load event is blocked by this object existing.
    doc: JS<Document>,
    /// The load that is blocking the document's load event.
    load: Option<LoadType>,
}

impl LoadBlocker {
    /// Mark the document's load event as blocked on this new load.
    pub fn new(doc: &Document, load: LoadType) -> LoadBlocker {
        doc.mut_loader().add_blocking_load(load.clone());
        LoadBlocker {
            doc: JS::from_ref(doc),
            load: Some(load),
        }
    }

    /// Remove this load from the associated document's list of blocking loads.
    pub fn terminate(blocker: &mut Option<LoadBlocker>) {
        if let Some(this) = blocker.as_mut() {
            this.doc.finish_load(this.load.take().unwrap());
        }
        *blocker = None;
    }

    /// Return the url associated with this load.
    pub fn url(&self) -> Option<&ServoUrl> {
        self.load.as_ref().map(LoadType::url)
    }
}

impl Drop for LoadBlocker {
    fn drop(&mut self) {
        if !thread::panicking() {
            debug_assert!(self.load.is_none());
        }
    }
}

#[derive(HeapSizeOf, JSTraceable)]
pub struct DocumentLoader {
    resource_threads: ResourceThreads,
    blocking_loads: Vec<LoadType>,
    events_inhibited: bool,
}

impl DocumentLoader {
    pub fn new(existing: &DocumentLoader) -> DocumentLoader {
        DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
    }

    pub fn new_with_threads(resource_threads: ResourceThreads,
                            initial_load: Option<ServoUrl>) -> DocumentLoader {
        debug!("Initial blocking load {:?}.", initial_load);
        let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect();

        DocumentLoader {
            resource_threads: resource_threads,
            blocking_loads: initial_loads,
            events_inhibited: false,
        }
    }

    /// Add a load to the list of blocking loads.
    fn add_blocking_load(&mut self, load: LoadType) {
        debug!("Adding blocking load {:?} ({}).", load, self.blocking_loads.len());
        self.blocking_loads.push(load);
    }

    /// Initiate a new fetch.
    pub fn fetch_async(&mut self,
                       load: LoadType,
                       request: RequestInit,
                       fetch_target: IpcSender<FetchResponseMsg>) {
        self.add_blocking_load(load);
        self.fetch_async_background(request, fetch_target);
    }

    /// Initiate a new fetch that does not block the document load event.
    pub fn fetch_async_background(&self,
                                  request: RequestInit,
                                  fetch_target: IpcSender<FetchResponseMsg>) {
        self.resource_threads.sender().send(CoreResourceMsg::Fetch(request, fetch_target)).unwrap();
    }

    /// Mark an in-progress network request complete.
    pub fn finish_load(&mut self, load: &LoadType) {
        debug!("Removing blocking load {:?} ({}).", load, self.blocking_loads.len());
        let idx = self.blocking_loads.iter().position(|unfinished| *unfinished == *load);
        self.blocking_loads.remove(idx.unwrap_or_else(|| panic!("unknown completed load {:?}", load)));
    }

    pub fn is_blocked(&self) -> bool {
        // TODO: Ensure that we report blocked if parsing is still ongoing.
        !self.blocking_loads.is_empty()
    }

    pub fn inhibit_events(&mut self) {
        self.events_inhibited = true;
    }

    pub fn events_inhibited(&self) -> bool {
        self.events_inhibited
    }

    pub fn resource_threads(&self) -> &ResourceThreads {
        &self.resource_threads
    }
}
<|endoftext|>


<commit_before>use animation::Animation;
use utils;

use glm;
use glm::ext::normalize_to;
use moho::errors::*;
use moho::frame_animator::FrameAnimator;
use moho::renderer::Renderer;
use moho::resource_manager::{ResourceManager, Texture};
use moho::tile_sheet::TileSheet;
use sdl2::rect;

use std::time::Duration;

pub struct Drawable {
    pub center: glm::IVec2,
    max_coords: glm::UVec2,
    animation: Animation,
    meteor: Texture,
    explosion: Texture,
    rects: [rect::Rect; 10],
}

impl Drawable {
    pub fn new<R: Renderer>(center: glm::IVec2,
                            max_coords: glm::UVec2,
                            resource_manager: &ResourceManager<R>)
                            -> Result<Self> {
        const NUM_FRAMES: u32 = 8;
        let meteor = resource_manager.load_texture("resources/meteor.png")?;
        let mut explosion = resource_manager.load_texture("resources/explosion_large.png")?;
        let frame_duration = Duration::from_millis(80_u64);
        let tile_sheet = TileSheet::new(glm::uvec2(NUM_FRAMES, 1));
        let animator = FrameAnimator::new(NUM_FRAMES, frame_duration, false);
        let animation = Animation::new(tile_sheet, animator);
        explosion.dims.x /= NUM_FRAMES;

        let drawable = Drawable {
            center: center,
            max_coords: max_coords,
            animation: animation,
            meteor: meteor,
            explosion: explosion,
            rects: [rect::Rect::new(0, 0, 5, 5); 10],
        };

        Ok(drawable)
    }

    pub fn animate_explosion(&mut self) {
        self.animation.update();
    }

    pub fn update_launch_vector(&mut self, target: glm::IVec2) {
        let target = glm::to_dvec2(target);
        let center = glm::to_dvec2(self.center);
        let distance = target - center;
        let offset = self.meteor.dims.x / 2 + 10;
        let offset_vector = normalize_to(distance, offset as f64);
        let anchor_point = center + offset_vector;
        let step = (target - anchor_point) / (self.rects.len() as f64);

        for (i, rect) in self.rects.iter_mut().enumerate() {
            let point = glm::to_ivec2(anchor_point + (step * i as f64));
            rect.center_on((point.x, point.y));
        }
    }

    pub fn meteor_dims(&self) -> glm::UVec2 {
        self.meteor.dims
    }

    pub fn draw_unlaunched<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        self.draw_meteor(renderer)?;
        renderer.fill_rects(&self.rects)
    }

    pub fn draw_meteor<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        self.draw(&self.meteor, None, renderer)
    }

    pub fn draw_explosion<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        let src_rect = Some(self.animation.src_rect());
        self.draw(&self.explosion, src_rect, renderer)
    }

    pub fn is_exploding(&self) -> bool {
        self.animation.is_active()
    }

    fn draw<R>(&self,
               texture: &Texture,
               src: Option<glm::DVec4>,
               renderer: &mut ResourceManager<R>)
               -> Result<()>
        where R: Renderer
    {
        let max_coords = Some(self.max_coords);
        let rect = utils::rect_from_center(self.center, texture.dims);
        renderer.draw(texture.id, Some(rect), src, max_coords)
    }
}
<commit_msg>use asset on meteor drawable<commit_after>use animation::Animation;
use asset::Asset;

use glm;
use glm::ext::normalize_to;
use moho::errors::*;
use moho::frame_animator::FrameAnimator;
use moho::renderer::Renderer;
use moho::resource_manager::ResourceManager;
use moho::tile_sheet::TileSheet;
use sdl2::rect;

use std::time::Duration;

pub struct Drawable {
    pub center: glm::IVec2,
    max_coords: glm::UVec2,
    animation: Animation,
    meteor: Asset,
    explosion: Asset,
    rects: [rect::Rect; 10],
}

impl Drawable {
    pub fn new<R: Renderer>(center: glm::IVec2,
                            max_coords: glm::UVec2,
                            resource_manager: &ResourceManager<R>)
                            -> Result<Self> {
        const NUM_FRAMES: u32 = 8;
        let meteor = resource_manager.load_texture("resources/meteor.png")?;
        let meteor = Asset::from_texture(&meteor);
        let explosion = resource_manager.load_texture("resources/explosion_large.png")?;
        let explosion_dims = glm::uvec2(explosion.dims.x / NUM_FRAMES, explosion.dims.y);
        let explosion = Asset::new(explosion.id, explosion_dims);
        let frame_duration = Duration::from_millis(80_u64);
        let tile_sheet = TileSheet::new(glm::uvec2(NUM_FRAMES, 1));
        let animator = FrameAnimator::new(NUM_FRAMES, frame_duration, false);
        let animation = Animation::new(tile_sheet, animator);

        let drawable = Drawable {
            center: center,
            max_coords: max_coords,
            animation: animation,
            meteor: meteor,
            explosion: explosion,
            rects: [rect::Rect::new(0, 0, 5, 5); 10],
        };

        Ok(drawable)
    }

    pub fn animate_explosion(&mut self) {
        self.animation.update();
    }

    pub fn update_launch_vector(&mut self, target: glm::IVec2) {
        let target = glm::to_dvec2(target);
        let center = glm::to_dvec2(self.center);
        let distance = target - center;
        let offset = self.meteor.dimensions.x / 2 + 10;
        let offset_vector = normalize_to(distance, offset as f64);
        let anchor_point = center + offset_vector;
        let step = (target - anchor_point) / (self.rects.len() as f64);

        for (i, rect) in self.rects.iter_mut().enumerate() {
            let point = glm::to_ivec2(anchor_point + (step * i as f64));
            rect.center_on((point.x, point.y));
        }
    }

    pub fn meteor_dims(&self) -> glm::UVec2 {
        self.meteor.dimensions
    }

    pub fn draw_unlaunched<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        self.draw_meteor(renderer)?;
        renderer.fill_rects(&self.rects)
    }

    pub fn draw_meteor<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        self.draw(&self.meteor, None, renderer)
    }

    pub fn draw_explosion<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        let src_rect = Some(self.animation.src_rect());
        self.draw(&self.explosion, src_rect, renderer)
    }

    pub fn is_exploding(&self) -> bool {
        self.animation.is_active()
    }

    fn draw<R>(&self,
               asset: &Asset,
               src: Option<glm::DVec4>,
               renderer: &mut ResourceManager<R>)
               -> Result<()>
        where R: Renderer
    {
        let max_coords = Some(self.max_coords);
        let rect = asset.dst_rect(self.center);
        renderer.draw(asset.texture_id, Some(rect), src, max_coords)
    }
}
<|endoftext|>