<commit_before>/// Contains all functionality pertaining to parsing, tokenizing, and generating input arguments.
pub mod errors;
mod jobs;
mod man;
mod quote;

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
use std::process::exit;
use permutate::Permutator;
use num_cpus;

use super::disk_buffer::{self, DiskBufferTrait};
use super::filepaths;
use super::input_iterator::InputIterator;
use super::tokenizer::{Token, tokenize};
use self::errors::ParseErr;

// Re-export key items from internal modules.
pub use self::errors::{FileErr, InputIteratorErr};

#[derive(PartialEq)]
enum Mode { Arguments, Command, Inputs, Files }

pub const INPUTS_ARE_COMMANDS: u8 = 1;
pub const PIPE_IS_ENABLED:     u8 = 2;
pub const SHELL_ENABLED:       u8 = 4;
pub const QUIET_MODE:          u8 = 8;
pub const VERBOSE_MODE:        u8 = 16;
pub const DASH_EXISTS:         u8 = 32;

/// Defines what quoting mode to use when expanding the command.
enum Quoting { None, Basic, Shell }

/// `Args` is a collection of critical options and arguments that were collected at
/// startup of the application.
pub struct Args {
    pub flags:        u8,
    pub ncores:       usize,
    pub arguments:    Vec<Token>,
    pub piped_values: Option<Vec<String>>,
    pub ninputs:      usize,
}

impl Args {
    pub fn new() -> Args {
        Args {
            ncores:       num_cpus::get(),
            flags:        SHELL_ENABLED,
            arguments:    Vec::new(),
            piped_values: None,
            ninputs:      0,
        }
    }

    pub fn parse(&mut self) -> Result<InputIterator, ParseErr> {
        let unprocessed_path = match filepaths::unprocessed() {
            Some(path) => path,
            None => return Err(ParseErr::File(FileErr::Path))
        };

        let mut quote = Quoting::None;

        // Create a write buffer that automatically writes data to the disk when the buffer is full.
        let mut disk_buffer = disk_buffer::DiskBuffer::new(&unprocessed_path).write()
            .map_err(|why| ParseErr::File(FileErr::Open(unprocessed_path.clone(), why)))?;

        // Temporary stores for input arguments.
        let mut raw_args                    = env::args().skip(1).peekable();
        let mut comm                        = String::with_capacity(128);
        let mut lists: Vec<Vec<String>>     = Vec::new();
        let mut current_inputs: Vec<String> = Vec::new();
        let mut number_of_arguments = 0;

        if env::args().len() > 1 {
            // The purpose of this is to set the initial parsing mode.
            let mut mode = match raw_args.peek().unwrap().as_ref() {
                ":::"  => Mode::Inputs,
                "::::" => Mode::Files,
                _      => Mode::Arguments
            };

            // If there are no arguments to be parsed, then the inputs are commands.
            if mode == Mode::Inputs || mode == Mode::Files {
                self.flags |= INPUTS_ARE_COMMANDS;
            } else {
                self.flags &= 255 * INPUTS_ARE_COMMANDS;
            }

            // Parse each and every input argument supplied to the program.
            while let Some(argument) = raw_args.next() {
                let argument = argument.as_str();
                match mode {
                    Mode::Arguments => {
                        let mut char_iter = argument.chars().peekable();

                        // If the first character is a '-' then it will be processed as an argument.
                        // We can guarantee that there will always be at least one character.
                        if char_iter.next().unwrap() == '-' {
                            // If the second character exists, everything's OK.
                            if let Some(character) = char_iter.next() {
                                // This scope of code allows users to utilize the GNU style
                                // command line arguments, to allow for laziness.
                                if character == 'j' {
                                    // The short-hand job argument needs to be handled specially.
                                    self.ncores = if char_iter.peek().is_some() {
                                        // Each character that follows after `j` will be considered an
                                        // input value.
                                        jobs::parse(&argument[2..])?
                                    } else {
                                        // If there was no character after `j`, the following argument
                                        // must be the job value.
                                        let val = &raw_args.next().ok_or(ParseErr::JobsNoValue)?;
                                        jobs::parse(val)?
                                    }
                                } else if character != '-' {
                                    // NOTE: Short mode versions of arguments
                                    for character in argument[1..].chars() {
                                        match character {
                                            'h' => {
                                                println!("{}", man::MAN_PAGE);
                                                exit(0);
                                            },
                                            'n' => self.flags &= 255 ^ SHELL_ENABLED,
                                            'p' => self.flags |= PIPE_IS_ENABLED,
                                            'q' => quote = Quoting::Basic,
                                            's' => self.flags |= QUIET_MODE,
                                            'v' => self.flags |= VERBOSE_MODE,
                                            _ => {
                                                return Err(ParseErr::InvalidArgument(argument.to_owned()))
                                            }
                                        }
                                    }
                                } else {
                                    // NOTE: Long mode versions of arguments
                                    match &argument[2..] {
                                        "help" => {
                                            println!("{}", man::MAN_PAGE);
                                            exit(0);
                                        },
                                        "jobs" => {
                                            let val = &raw_args.next().ok_or(ParseErr::JobsNoValue)?;
                                            self.ncores = jobs::parse(val)?
                                        },
                                        "no-shell" => self.flags &= 255 ^ SHELL_ENABLED,
                                        "num-cpu-cores" => {
                                            println!("{}", num_cpus::get());
                                            exit(0);
                                        },
                                        "pipe" => self.flags |= PIPE_IS_ENABLED,
                                        "quiet" | "silent" => self.flags |= QUIET_MODE,
                                        "quote" => quote = Quoting::Basic,
                                        "shellquote" => quote = Quoting::Shell,
                                        "verbose" => self.flags |= VERBOSE_MODE,
                                        "version" => {
                                            println!("parallel 0.6.2\n\nCrate Dependencies:");
                                            println!("    libc      0.2.15");
                                            println!("    num_cpus  1.0.0");
                                            println!("    permutate 0.1.3");
                                            exit(0);
                                        }
                                        _ => {
                                            return Err(ParseErr::InvalidArgument(argument.to_owned()));
                                        }
                                    }
                                }
                            } else {
                                // `-` will never be a valid argument
                                return Err(ParseErr::InvalidArgument("-".to_owned()));
                            }
                        } else {
                            match argument {
                                ":::" => {
                                    mode = Mode::Inputs;
                                    self.flags |= INPUTS_ARE_COMMANDS;
                                },
                                "::::" => {
                                    mode = Mode::Files;
                                    self.flags |= INPUTS_ARE_COMMANDS;
                                }
                                _ => {
                                    // The command has been supplied, and argument parsing is over.
                                    comm.push_str(argument);
                                    mode = Mode::Command;
                                }
                            }
                        }
                    },
                    Mode::Command => match argument {
                        // Arguments after `:::` are input values.
                        ":::" | ":::+" => mode = Mode::Inputs,
                        // Arguments after `::::` are files with inputs.
                        "::::" | "::::+" => mode = Mode::Files,
                        // All other arguments are command arguments.
                        _ => {
                            comm.push(' ');
                            comm.push_str(argument);
                        }
                    },
                    _ => match argument {
                        // `:::` denotes that the next set of inputs will be added to a new list.
                        ":::"  => {
                            mode = Mode::Inputs;
                            if !current_inputs.is_empty() {
                                lists.push(current_inputs.clone());
                                current_inputs.clear();
                            }
                        },
                        // `:::+` denotes that the next set of inputs will be added to the current list.
                        ":::+" => mode = Mode::Inputs,
                        // `::::` denotes that the next set of inputs will be added to a new list.
                        "::::"  => {
                            mode = Mode::Files;
                            if !current_inputs.is_empty() {
                                lists.push(current_inputs.clone());
                                current_inputs.clear();
                            }
                        },
                        // `:::+` denotes that the next set of inputs will be added to the current list.
                        "::::+" => mode = Mode::Files,
                        // All other arguments will be added to the current list.
                        _ => match mode {
                            Mode::Inputs => current_inputs.push(argument.to_owned()),
                            Mode::Files => file_parse(&mut current_inputs, argument)?,
                            _ => unreachable!()
                        }
                    }
                }
            }

            if !current_inputs.is_empty() {
                lists.push(current_inputs.clone());
            }

            if lists.len() > 1 {
                // Convert the Vec<Vec<String>> into a Vec<Vec<&str>>
                let tmp: Vec<Vec<&str>> = lists.iter()
                    .map(|list| list.iter().map(AsRef::as_ref).collect::<Vec<&str>>())
                    .collect();

                // Convert the Vec<Vec<&str>> into a Vec<&[&str]>
                let list_array: Vec<&[&str]> = tmp.iter().map(AsRef::as_ref).collect();

                // Create a `Permutator` with the &[&[&str]] as the input.
                let permutator = Permutator::new(&list_array[..]);

                for permutation in permutator {
                    let mut iter = permutation.iter();
                    disk_buffer.write(iter.next().unwrap().as_bytes())
                        .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    for element in iter {
                        disk_buffer.write_byte(b' ')
                            .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                        disk_buffer.write(element.as_bytes())
                            .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    }
                    disk_buffer.write_byte(b'\n')
                        .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            } else {
                for input in current_inputs {
                    disk_buffer.write(input.as_bytes()).map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    disk_buffer.write_byte(b'\n').map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            }
        }

        // If no inputs are provided, read from stdin instead.
        if disk_buffer.is_empty() {
            let stdin = io::stdin();
            for line in stdin.lock().lines() {
                if let Ok(line) = line {
                    disk_buffer.write(line.as_bytes()).map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    disk_buffer.write_byte(b'\n').map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            }
        }

        if number_of_arguments == 0 { return Err(ParseErr::NoArguments); }

        // Flush the contents of the buffer to the disk before tokenizing the command argument.
        disk_buffer.flush().map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;

        // Expand the command if quoting is enabled
        match quote {
            Quoting::None  => (),
            Quoting::Basic => comm = quote::basic(comm),
            Quoting::Shell => comm = quote::shell(comm),
        }

        // Attempt to tokenize the command argument into simple primitive placeholders.
        tokenize(&mut self.arguments, &comm, &unprocessed_path, number_of_arguments).map_err(ParseErr::Token)?;

        if dash_exists() {
            self.flags |= DASH_EXISTS;
        }

        if !shell_required(&self.arguments) {
            self.flags &= 255 ^ SHELL_ENABLED;
        }

        // Return an `InputIterator` of the arguments contained within the unprocessed file.
        let path = filepaths::unprocessed().ok_or(ParseErr::File(FileErr::Path))?;
        Ok(InputIterator::new(&path, number_of_arguments).map_err(ParseErr::File)?)
    }
}

fn shell_required(arguments: &[Token]) -> bool {
    for token in arguments {
        if let &Token::Argument(ref arg) = token {
            if arg.contains(';') || arg.contains('&') {
                return true
            }
        }
    }
    false
}

fn dash_exists() -> bool {
    if let Ok(path) = env::var("PATH") {
        for path in path.split(':') {
            if let Ok(directory) = fs::read_dir(path) {
                for entry in directory {
                    if let Ok(entry) = entry {
                        let path = entry.path();
                        if path.is_file() && path.file_name() == Some(OsStr::new("dash")) { return true; }
                    }
                }
            }
        }
    }
    false
}

/// Attempts to open an input argument and adds each line to the `inputs` list.
fn file_parse<P: AsRef<Path>>(inputs: &mut Vec<String>, path: P) -> Result<(), ParseErr> {
    let path = path.as_ref();
    let file = fs::File::open(path).map_err(|err| ParseErr::File(FileErr::Open(path.to_owned(), err)))?;
    for line in BufReader::new(file).lines() {
        if let Ok(line) = line { inputs.push(line); }
    }
    Ok(())
}
<commit_msg>This wasn't supposed to happen But it somehow seems to not cause any problems?<commit_after>/// Contains all functionality pertaining to parsing, tokenizing, and generating input arguments.
pub mod errors;
mod jobs;
mod man;
mod quote;

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
use std::process::exit;
use permutate::Permutator;
use num_cpus;

use super::disk_buffer::{self, DiskBufferTrait};
use super::filepaths;
use super::input_iterator::InputIterator;
use super::tokenizer::{Token, tokenize};
use self::errors::ParseErr;

// Re-export key items from internal modules.
pub use self::errors::{FileErr, InputIteratorErr};

#[derive(PartialEq)]
enum Mode { Arguments, Command, Inputs, Files }

pub const INPUTS_ARE_COMMANDS: u8 = 1;
pub const PIPE_IS_ENABLED:     u8 = 2;
pub const SHELL_ENABLED:       u8 = 4;
pub const QUIET_MODE:          u8 = 8;
pub const VERBOSE_MODE:        u8 = 16;
pub const DASH_EXISTS:         u8 = 32;

/// Defines what quoting mode to use when expanding the command.
enum Quoting { None, Basic, Shell }

/// `Args` is a collection of critical options and arguments that were collected at
/// startup of the application.
pub struct Args {
    pub flags:        u8,
    pub ncores:       usize,
    pub arguments:    Vec<Token>,
    pub piped_values: Option<Vec<String>>,
    pub ninputs:      usize,
}

impl Args {
    pub fn new() -> Args {
        Args {
            ncores:       num_cpus::get(),
            flags:        SHELL_ENABLED,
            arguments:    Vec::new(),
            piped_values: None,
            ninputs:      0,
        }
    }

    pub fn parse(&mut self) -> Result<InputIterator, ParseErr> {
        let unprocessed_path = match filepaths::unprocessed() {
            Some(path) => path,
            None => return Err(ParseErr::File(FileErr::Path))
        };

        let mut quote = Quoting::None;

        // Create a write buffer that automatically writes data to the disk when the buffer is full.
        let mut disk_buffer = disk_buffer::DiskBuffer::new(&unprocessed_path).write()
            .map_err(|why| ParseErr::File(FileErr::Open(unprocessed_path.clone(), why)))?;

        // Temporary stores for input arguments.
        let mut raw_args                    = env::args().skip(1).peekable();
        let mut comm                        = String::with_capacity(128);
        let mut lists: Vec<Vec<String>>     = Vec::new();
        let mut current_inputs: Vec<String> = Vec::new();
        let mut number_of_arguments = 0;

        if env::args().len() > 1 {
            // The purpose of this is to set the initial parsing mode.
            let mut mode = match raw_args.peek().unwrap().as_ref() {
                ":::"  => Mode::Inputs,
                "::::" => Mode::Files,
                _      => Mode::Arguments
            };

            // If there are no arguments to be parsed, then the inputs are commands.
            if mode == Mode::Inputs || mode == Mode::Files {
                self.flags |= INPUTS_ARE_COMMANDS;
            } else {
                self.flags &= 255 ^ INPUTS_ARE_COMMANDS;
            }

            // Parse each and every input argument supplied to the program.
            while let Some(argument) = raw_args.next() {
                let argument = argument.as_str();
                match mode {
                    Mode::Arguments => {
                        let mut char_iter = argument.chars().peekable();

                        // If the first character is a '-' then it will be processed as an argument.
                        // We can guarantee that there will always be at least one character.
                        if char_iter.next().unwrap() == '-' {
                            // If the second character exists, everything's OK.
                            if let Some(character) = char_iter.next() {
                                // This scope of code allows users to utilize the GNU style
                                // command line arguments, to allow for laziness.
                                if character == 'j' {
                                    // The short-hand job argument needs to be handled specially.
                                    self.ncores = if char_iter.peek().is_some() {
                                        // Each character that follows after `j` will be considered an
                                        // input value.
                                        jobs::parse(&argument[2..])?
                                    } else {
                                        // If there was no character after `j`, the following argument
                                        // must be the job value.
                                        let val = &raw_args.next().ok_or(ParseErr::JobsNoValue)?;
                                        jobs::parse(val)?
                                    }
                                } else if character != '-' {
                                    // NOTE: Short mode versions of arguments
                                    for character in argument[1..].chars() {
                                        match character {
                                            'h' => {
                                                println!("{}", man::MAN_PAGE);
                                                exit(0);
                                            },
                                            'n' => self.flags &= 255 ^ SHELL_ENABLED,
                                            'p' => self.flags |= PIPE_IS_ENABLED,
                                            'q' => quote = Quoting::Basic,
                                            's' => self.flags |= QUIET_MODE,
                                            'v' => self.flags |= VERBOSE_MODE,
                                            _ => {
                                                return Err(ParseErr::InvalidArgument(argument.to_owned()))
                                            }
                                        }
                                    }
                                } else {
                                    // NOTE: Long mode versions of arguments
                                    match &argument[2..] {
                                        "help" => {
                                            println!("{}", man::MAN_PAGE);
                                            exit(0);
                                        },
                                        "jobs" => {
                                            let val = &raw_args.next().ok_or(ParseErr::JobsNoValue)?;
                                            self.ncores = jobs::parse(val)?
                                        },
                                        "no-shell" => self.flags &= 255 ^ SHELL_ENABLED,
                                        "num-cpu-cores" => {
                                            println!("{}", num_cpus::get());
                                            exit(0);
                                        },
                                        "pipe" => self.flags |= PIPE_IS_ENABLED,
                                        "quiet" | "silent" => self.flags |= QUIET_MODE,
                                        "quote" => quote = Quoting::Basic,
                                        "shellquote" => quote = Quoting::Shell,
                                        "verbose" => self.flags |= VERBOSE_MODE,
                                        "version" => {
                                            println!("parallel 0.6.2\n\nCrate Dependencies:");
                                            println!("    libc      0.2.15");
                                            println!("    num_cpus  1.0.0");
                                            println!("    permutate 0.1.3");
                                            exit(0);
                                        }
                                        _ => {
                                            return Err(ParseErr::InvalidArgument(argument.to_owned()));
                                        }
                                    }
                                }
                            } else {
                                // `-` will never be a valid argument
                                return Err(ParseErr::InvalidArgument("-".to_owned()));
                            }
                        } else {
                            match argument {
                                ":::" => {
                                    mode = Mode::Inputs;
                                    self.flags |= INPUTS_ARE_COMMANDS;
                                },
                                "::::" => {
                                    mode = Mode::Files;
                                    self.flags |= INPUTS_ARE_COMMANDS;
                                }
                                _ => {
                                    // The command has been supplied, and argument parsing is over.
                                    comm.push_str(argument);
                                    mode = Mode::Command;
                                }
                            }
                        }
                    },
                    Mode::Command => match argument {
                        // Arguments after `:::` are input values.
                        ":::" | ":::+" => mode = Mode::Inputs,
                        // Arguments after `::::` are files with inputs.
                        "::::" | "::::+" => mode = Mode::Files,
                        // All other arguments are command arguments.
                        _ => {
                            comm.push(' ');
                            comm.push_str(argument);
                        }
                    },
                    _ => match argument {
                        // `:::` denotes that the next set of inputs will be added to a new list.
                        ":::"  => {
                            mode = Mode::Inputs;
                            if !current_inputs.is_empty() {
                                lists.push(current_inputs.clone());
                                current_inputs.clear();
                            }
                        },
                        // `:::+` denotes that the next set of inputs will be added to the current list.
                        ":::+" => mode = Mode::Inputs,
                        // `::::` denotes that the next set of inputs will be added to a new list.
                        "::::"  => {
                            mode = Mode::Files;
                            if !current_inputs.is_empty() {
                                lists.push(current_inputs.clone());
                                current_inputs.clear();
                            }
                        },
                        // `:::+` denotes that the next set of inputs will be added to the current list.
                        "::::+" => mode = Mode::Files,
                        // All other arguments will be added to the current list.
                        _ => match mode {
                            Mode::Inputs => current_inputs.push(argument.to_owned()),
                            Mode::Files => file_parse(&mut current_inputs, argument)?,
                            _ => unreachable!()
                        }
                    }
                }
            }

            if !current_inputs.is_empty() {
                lists.push(current_inputs.clone());
            }

            if lists.len() > 1 {
                // Convert the Vec<Vec<String>> into a Vec<Vec<&str>>
                let tmp: Vec<Vec<&str>> = lists.iter()
                    .map(|list| list.iter().map(AsRef::as_ref).collect::<Vec<&str>>())
                    .collect();

                // Convert the Vec<Vec<&str>> into a Vec<&[&str]>
                let list_array: Vec<&[&str]> = tmp.iter().map(AsRef::as_ref).collect();

                // Create a `Permutator` with the &[&[&str]] as the input.
                let permutator = Permutator::new(&list_array[..]);

                for permutation in permutator {
                    let mut iter = permutation.iter();
                    disk_buffer.write(iter.next().unwrap().as_bytes())
                        .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    for element in iter {
                        disk_buffer.write_byte(b' ')
                            .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                        disk_buffer.write(element.as_bytes())
                            .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    }
                    disk_buffer.write_byte(b'\n')
                        .map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            } else {
                for input in current_inputs {
                    disk_buffer.write(input.as_bytes()).map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    disk_buffer.write_byte(b'\n').map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            }
        }

        // If no inputs are provided, read from stdin instead.
        if disk_buffer.is_empty() {
            let stdin = io::stdin();
            for line in stdin.lock().lines() {
                if let Ok(line) = line {
                    disk_buffer.write(line.as_bytes()).map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    disk_buffer.write_byte(b'\n').map_err(|why|
                        ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;
                    number_of_arguments += 1;
                }
            }
        }

        if number_of_arguments == 0 { return Err(ParseErr::NoArguments); }

        // Flush the contents of the buffer to the disk before tokenizing the command argument.
        disk_buffer.flush().map_err(|why| ParseErr::File(FileErr::Write(disk_buffer.path.clone(), why)))?;

        // Expand the command if quoting is enabled
        match quote {
            Quoting::None  => (),
            Quoting::Basic => comm = quote::basic(comm),
            Quoting::Shell => comm = quote::shell(comm),
        }

        // Attempt to tokenize the command argument into simple primitive placeholders.
        tokenize(&mut self.arguments, &comm, &unprocessed_path, number_of_arguments).map_err(ParseErr::Token)?;

        if dash_exists() {
            self.flags |= DASH_EXISTS;
        }

        if !shell_required(&self.arguments) {
            self.flags &= 255 ^ SHELL_ENABLED;
        }

        // Return an `InputIterator` of the arguments contained within the unprocessed file.
        let path = filepaths::unprocessed().ok_or(ParseErr::File(FileErr::Path))?;
        Ok(InputIterator::new(&path, number_of_arguments).map_err(ParseErr::File)?)
    }
}

fn shell_required(arguments: &[Token]) -> bool {
    for token in arguments {
        if let &Token::Argument(ref arg) = token {
            if arg.contains(';') || arg.contains('&') {
                return true
            }
        }
    }
    false
}

fn dash_exists() -> bool {
    if let Ok(path) = env::var("PATH") {
        for path in path.split(':') {
            if let Ok(directory) = fs::read_dir(path) {
                for entry in directory {
                    if let Ok(entry) = entry {
                        let path = entry.path();
                        if path.is_file() && path.file_name() == Some(OsStr::new("dash")) { return true; }
                    }
                }
            }
        }
    }
    false
}

/// Attempts to open an input argument and adds each line to the `inputs` list.
fn file_parse<P: AsRef<Path>>(inputs: &mut Vec<String>, path: P) -> Result<(), ParseErr> {
    let path = path.as_ref();
    let file = fs::File::open(path).map_err(|err| ParseErr::File(FileErr::Open(path.to_owned(), err)))?;
    for line in BufReader::new(file).lines() {
        if let Ok(line) = line { inputs.push(line); }
    }
    Ok(())
}
<|endoftext|>


<commit_before>extern crate timely;
extern crate graph_map;
extern crate alg3_dynamic;

use std::sync::{Arc, Mutex};

use alg3_dynamic::*;

use timely::dataflow::operators::*;

use graph_map::GraphMMap;

#[allow(non_snake_case)]
fn main () {

    let start = ::std::time::Instant::now();

    let send = Arc::new(Mutex::new(0));
    let send2 = send.clone();

    let inspect = ::std::env::args().find(|x| x == "inspect").is_some();

    timely::execute_from_args(std::env::args(), move |root| {

        let send = send.clone();

        // used to partition graph loading
        let index = root.index();
        let peers = root.peers();

        // handles to input and probe, but also both indices so we can compact them.
        let (mut input, probe, forward, reverse) = root.dataflow::<u32,_,_>(|builder| {

            // A stream of changes to the set of *triangles*, where a < b < c.
            let (graph, dT) = builder.new_input::<((u32, u32, u32), i32)>();

            // Our query is K4(w,x,y,z) := T(w,x,y), T(w,x,z), T(w,y,z), T(x,y,z)
            //
            // This query is technically redundant, because the middle two constraints imply the fourth,
            // so let's slim it down to
            //
            //    K4(w,x,y,z) := T(w,x,y), T(w,x,z), T(w,y,z)
            //
            // This seems like it could be a bit more complicated than triangles, in determining the rules
            // for incremental updates. I'm going to write them down first, and we'll see which indices we
            // actually need. I'll use A, B, and C for the instances of T above.
            //
            //    dK4dA(w,x,y,z) := dA(w,x,y), B(w,x,z), C(w,y,z)
            //    dK4dB(w,x,y,z) := dB(w,x,z), A(w,x,y), C(w,y,z)
            //    dK4dC(w,x,y,z) := dC(w,y,z), A(w,x,y), B(w,x,z)
            //
            // Looking at this, it seems like we will need
            //
            //    dK4dA : indices on (w,x,_) and (w,_,y)
            //    dK4dB : indices on (w,x,_) and (w,_,z)
            //    dK4dC : indices on (w,_,y) and (w,_,z)
            //
            // All of this seems to boil down to a "forward" and a "reverse" index, just as for triangles,
            // but where `w` is always present as part of the key. We just might want the first or second
            // field that follows it.

            // create two indices, one "forward" from (a,b) to c, and one "reverse" from (a,c) to b.
            let forward = IndexStream::from(|(a,b)| (a + b) as u64, &Vec::new().to_stream(builder), &dT.map(|((a,b,c),wgt)| (((a,b),c),wgt)));
            let reverse = IndexStream::from(|(a,c)| (a + c) as u64, &Vec::new().to_stream(builder), &dT.map(|((a,b,c),wgt)| (((a,c),b),wgt)));

            // dK4dA(w,x,y,z) := dA(w,x,y), B(w,x,z), C(w,y,z)
            let dK4dA = dT.extend(vec![Box::new(forward.extend_using(|&(w,x,y)| (w,x), <_ as PartialOrd>::lt)),
                                       Box::new(forward.extend_using(|&(w,x,y)| (w,y), <_ as PartialOrd>::lt))])
                          .flat_map(|((w,x,y), zs, wgt)| zs.into_iter().map(move |z| ((w,x,y,z),wgt)));

            // dK4dB(w,x,y,z) := dB(w,x,z), A(w,x,y), C(w,y,z)
            let dK4dB = dT.extend(vec![Box::new(forward.extend_using(|&(w,x,z)| (w,x), <_ as PartialOrd>::le)),
                                       Box::new(reverse.extend_using(|&(w,x,z)| (w,z), <_ as PartialOrd>::lt))])
                          .flat_map(|((w,x,z), ys, wgt)| ys.into_iter().map(move |y| ((w,x,y,z),wgt)));

            // dK4dC(w,x,y,z) := dC(w,y,z), A(w,x,y), B(w,x,z)
            let dK4dC = dT.extend(vec![Box::new(reverse.extend_using(|&(w,y,z)| (w,y), <_ as PartialOrd>::le)),
                                       Box::new(reverse.extend_using(|&(w,y,z)| (w,z), <_ as PartialOrd>::le))])
                          .flat_map(|((w,y,z), xs, wgt)| xs.into_iter().map(move |x| ((w,x,y,z),wgt)));

            let dK4 = dK4dA.concat(&dK4dB).concat(&dK4dC);

            // if the third argument is "inspect", report triangle counts.
            if inspect {
                dK4.exchange(|x| (x.0).0 as u64)
                       // .inspect_batch(|t,x| println!("{:?}: {:?}", t, x))
                       .count()
                       .inspect_batch(move |t,x| println!("{:?}: {:?}", t, x))
                       .inspect_batch(move |_,x| {
                            if let Ok(mut bound) = send.lock() {
                                *bound += x[0];
                            }
                        });
            }

            (graph, dK4.probe(), forward, reverse)
        });

        // load fragment of input graph into memory to avoid io while running.
        let filename = std::env::args().nth(1).unwrap();
        let graph = GraphMMap::new(&filename);

        let nodes = graph.nodes();
        let mut triangles = Vec::new();

        let mut v1 = root.index();
        while v1 < graph.nodes() {
            let v1f = graph.edges(v1);
            for (index_v2, &v2) in v1f.iter().enumerate() {
                intersect_and(&v1f[(index_v2+1)..], graph.edges(v2 as usize), |v3| triangles.push((v1 as u32, v2, v3)));
            }
            v1 += root.peers();
        }

        drop(graph);

        println!("{:?}\tworker {} computed {} triangles", start.elapsed(), root.index(), triangles.len());

        // synchronize with other workers.
        let prev = input.time().clone();
        input.advance_to(prev.inner + 1);
        root.step_while(|| probe.less_than(input.time()));

        // number of nodes introduced at a time
        let batch: usize = std::env::args().nth(2).unwrap().parse().unwrap();

        // start the experiment!
        let start = ::std::time::Instant::now();

        let mut sent = 0;
        while sent < triangles.len() {
            let to_send = std::cmp::min(batch / root.peers(), triangles.len() - sent);
            for off in 0 .. to_send {
                input.send((triangles[sent + off], 1));
            }
            sent += to_send;

            let prev = input.time().clone();
            input.advance_to(prev.inner + 1);
            while probe.less_than(input.time()) { root.step(); }

            // merge all of the indices we maintain.
            forward.index.borrow_mut().merge_to(&prev);
            reverse.index.borrow_mut().merge_to(&prev);
        }

        input.close();
        while root.step() { }

        if inspect {
            println!("worker {} elapsed: {:?}", index, start.elapsed());
        }

    }).unwrap();

    let total = if let Ok(lock) = send2.lock() {
        *lock
    }
    else { 0 };

    if inspect {
        println!("elapsed: {:?}\ttotal triangles at this process: {:?}", start.elapsed(), total);
    }
}

fn intersect_and<F: FnMut(u32)>(aaa: &[u32], mut bbb: &[u32], mut func: F) {

    if aaa.len() > bbb.len() {
        intersect_and(bbb, aaa, func);
    }
    else {
        if aaa.len() < bbb.len() / 16 {
            for &a in aaa.iter() {
                bbb = gallop_ge(bbb, &a);
                if bbb.len() > 0 && bbb[0] == a {
                    func(a)
                }
            }
        }
        else {
            for &a in aaa.iter() {
                while bbb.len() > 0 && bbb[0] < a {
                    bbb = &bbb[1..];
                }
                if bbb.len() > 0 && a == bbb[0] {
                    func(a);
                }
            }
        }
    }
}

#[inline(always)]
pub fn gallop_ge<'a, T: Ord>(mut slice: &'a [T], value: &T) -> &'a [T] {
    // if empty slice, or already >= element, return
    if slice.len() > 0 && &slice[0] < value {
        let mut step = 1;
        while step < slice.len() && &slice[step] < value {
            slice = &slice[step..];
            step = step << 1;
        }

        step = step >> 1;
        while step > 0 {
            if step < slice.len() && &slice[step] < value {
                slice = &slice[step..];
            }
            step = step >> 1;
        }

        slice = &slice[1..]; // advance one, as we always stayed < value
    }

    return slice;
}

#[inline(always)]
pub fn gallop_gt<'a, T: Ord>(mut slice: &'a [T], value: &T) -> &'a [T] {
    // if empty slice, or already > element, return
    if slice.len() > 0 && &slice[0] <= value {
        let mut step = 1;
        while step < slice.len() && &slice[step] <= value {
            slice = &slice[step..];
            step = step << 1;
        }

        step = step >> 1;
        while step > 0 {
            if step < slice.len() && &slice[step] <= value {
                slice = &slice[step..];
            }
            step = step >> 1;
        }

        slice = &slice[1..]; // advance one, as we always stayed <= value
    }

    return slice;
}<commit_msg>get right answer out<commit_after>extern crate timely;
extern crate graph_map;
extern crate alg3_dynamic;

use std::sync::{Arc, Mutex};

use alg3_dynamic::*;

use timely::dataflow::operators::*;

use graph_map::GraphMMap;

#[allow(non_snake_case)]
fn main () {

    let start = ::std::time::Instant::now();

    let send = Arc::new(Mutex::new(0));
    let send2 = send.clone();

    let inspect = ::std::env::args().find(|x| x == "inspect").is_some();

    timely::execute_from_args(std::env::args(), move |root| {

        let send = send.clone();

        // used to partition graph loading
        let index = root.index();
        let peers = root.peers();

        // handles to input and probe, but also both indices so we can compact them.
        let (mut input, probe, forward, reverse) = root.dataflow::<u32,_,_>(|builder| {

            // A stream of changes to the set of *triangles*, where a < b < c.
            let (graph, dT) = builder.new_input::<((u32, u32, u32), i32)>();

            // Our query is K4(w,x,y,z) := T(w,x,y), T(w,x,z), T(w,y,z), T(x,y,z)
            //
            // This query is technically redundant, because the middle two constraints imply the fourth,
            // so let's slim it down to
            //
            //    K4(w,x,y,z) := T(w,x,y), T(w,x,z), T(w,y,z)
            //
            // This seems like it could be a bit more complicated than triangles, in determining the rules
            // for incremental updates. I'm going to write them down first, and we'll see which indices we
            // actually need. I'll use A, B, and C for the instances of T above.
            //
            //    dK4dA(w,x,y,z) := dA(w,x,y), B(w,x,z), C(w,y,z)
            //    dK4dB(w,x,y,z) := dB(w,x,z), A(w,x,y), C(w,y,z)
            //    dK4dC(w,x,y,z) := dC(w,y,z), A(w,x,y), B(w,x,z)
            //
            // Looking at this, it seems like we will need
            //
            //    dK4dA : indices on (w,x,_) and (w,_,y)
            //    dK4dB : indices on (w,x,_) and (w,_,z)
            //    dK4dC : indices on (w,_,y) and (w,_,z)
            //
            // All of this seems to boil down to a "forward" and a "reverse" index, just as for triangles,
            // but where `w` is always present as part of the key. We just might want the first or second
            // field that follows it.

            // create two indices, one "forward" from (a,b) to c, and one "reverse" from (a,c) to b.
            let forward = IndexStream::from(|(a,b)| (a + b) as u64, &Vec::new().to_stream(builder), &dT.map(|((a,b,c),wgt)| (((a,b),c),wgt)));
            let reverse = IndexStream::from(|(a,c)| (a + c) as u64, &Vec::new().to_stream(builder), &dT.map(|((a,b,c),wgt)| (((a,c),b),wgt)));

            // dK4dA(w,x,y,z) := dA(w,x,y), B(w,x,z), C(w,y,z)
            let dK4dA = dT.extend(vec![Box::new(forward.extend_using(|&(w,x,y)| (w,x), <_ as PartialOrd>::lt)),
                                       Box::new(forward.extend_using(|&(w,x,y)| (w,y), <_ as PartialOrd>::lt))])
                          .flat_map(|((w,x,y), zs, wgt)| zs.into_iter().map(move |z| ((w,x,y,z),wgt)));

            // dK4dB(w,x,y,z) := dB(w,x,z), A(w,x,y), C(w,y,z)
            let dK4dB = dT.extend(vec![Box::new(forward.extend_using(|&(w,x,z)| (w,x), <_ as PartialOrd>::le)),
                                       Box::new(reverse.extend_using(|&(w,x,z)| (w,z), <_ as PartialOrd>::lt))])
                          .flat_map(|((w,x,z), ys, wgt)| ys.into_iter().map(move |y| ((w,x,y,z),wgt)));

            // dK4dC(w,x,y,z) := dC(w,y,z), A(w,x,y), B(w,x,z)
            let dK4dC = dT.extend(vec![Box::new(reverse.extend_using(|&(w,y,z)| (w,y), <_ as PartialOrd>::le)),
                                       Box::new(reverse.extend_using(|&(w,y,z)| (w,z), <_ as PartialOrd>::le))])
                          .flat_map(|((w,y,z), xs, wgt)| xs.into_iter().map(move |x| ((w,x,y,z),wgt)));

            let dK4 = dK4dA.concat(&dK4dB).concat(&dK4dC);

            // if the third argument is "inspect", report triangle counts.
            if inspect {
                dK4.exchange(|x| (x.0).0 as u64)
                       // .inspect_batch(|t,x| println!("{:?}: {:?}", t, x))
                       .count()
                       .inspect_batch(move |t,x| println!("{:?}: {:?}", t, x))
                       .inspect_batch(move |_,x| {
                            if let Ok(mut bound) = send.lock() {
                                *bound += x[0];
                            }
                        });
            }

            (graph, dK4.probe(), forward, reverse)
        });

        // load fragment of input graph into memory to avoid io while running.
        let filename = std::env::args().nth(1).unwrap();
        let graph = GraphMap::new(&filename);

        let nodes = graph.nodes();
        let mut triangles = Vec::new();

        let mut v1 = root.index() as u32;
        while v1 < graph.nodes() {
            let v1f = graph.forward(v1);
            for (index_v2, &v2) in v1f.iter().enumerate() {
                intersect_and(&v1f[(index_v2+1)..], graph.forward(v2), |v3| triangles.push((v1 as u32, v2, v3)));
            }
            v1 += root.peers() as u32;
        }

        drop(graph);

        println!("{:?}\tworker {} computed {} triangles", start.elapsed(), root.index(), triangles.len());

        // synchronize with other workers.
        let prev = input.time().clone();
        input.advance_to(prev.inner + 1);
        root.step_while(|| probe.less_than(input.time()));

        // number of nodes introduced at a time
        let batch: usize = std::env::args().nth(2).unwrap().parse().unwrap();

        // start the experiment!
        let start = ::std::time::Instant::now();

        let mut sent = 0;
        while sent < triangles.len() {
            let to_send = std::cmp::min(batch / root.peers(), triangles.len() - sent);
            for off in 0 .. to_send {
                input.send((triangles[sent + off], 1));
            }
            sent += to_send;

            let prev = input.time().clone();
            input.advance_to(prev.inner + 1);
            while probe.less_than(input.time()) { root.step(); }

            // merge all of the indices we maintain.
            forward.index.borrow_mut().merge_to(&prev);
            reverse.index.borrow_mut().merge_to(&prev);
        }

        input.close();
        while root.step() { }

        if inspect {
            println!("worker {} elapsed: {:?}", index, start.elapsed());
        }

    }).unwrap();

    let total = if let Ok(lock) = send2.lock() {
        *lock
    }
    else { 0 };

    if inspect {
        println!("elapsed: {:?}\ttotal triangles at this process: {:?}", start.elapsed(), total);
    }
}

struct GraphMap {
    map: GraphMMap,
    reverse: Vec<u32>,
}

impl GraphMap {
    pub fn new(filename: &str) -> Self {

        let map = GraphMMap::new(filename);

        let mut reverse = vec![0; map.nodes()];
        for node in 0 .. map.nodes() {
            for &neighbor in map.edges(node) {
                if (neighbor as usize) < node {
                    reverse[node] += 1;
                }
                if (neighbor as usize) == node {
                    panic!("self-loop");
                }
            }
        }

        GraphMap {
            map: map,
            reverse: reverse,
        }
    }

    #[inline(always)]
    pub fn nodes(&self) -> u32 { self.map.nodes() as u32 }
    #[inline(always)]
    pub fn edges(&self, node: u32) -> &[u32] { self.map.edges(node as usize) }
    #[inline(always)]
    pub fn forward(&self, node: u32) -> &[u32] {
        &self.edges(node)[(self.reverse[node as usize] as usize)..]
    }
}

fn intersect_and<F: FnMut(u32)>(aaa: &[u32], mut bbb: &[u32], mut func: F) {

    if aaa.len() > bbb.len() {
        intersect_and(bbb, aaa, func);
    }
    else {
        if aaa.len() < bbb.len() / 16 {
            for &a in aaa.iter() {
                bbb = gallop_ge(bbb, &a);
                if bbb.len() > 0 && bbb[0] == a {
                    func(a)
                }
            }
        }
        else {
            for &a in aaa.iter() {
                while bbb.len() > 0 && bbb[0] < a {
                    bbb = &bbb[1..];
                }
                if bbb.len() > 0 && a == bbb[0] {
                    func(a);
                }
            }
        }
    }
}

#[inline(always)]
pub fn gallop_ge<'a, T: Ord>(mut slice: &'a [T], value: &T) -> &'a [T] {
    // if empty slice, or already >= element, return
    if slice.len() > 0 && &slice[0] < value {
        let mut step = 1;
        while step < slice.len() && &slice[step] < value {
            slice = &slice[step..];
            step = step << 1;
        }

        step = step >> 1;
        while step > 0 {
            if step < slice.len() && &slice[step] < value {
                slice = &slice[step..];
            }
            step = step >> 1;
        }

        slice = &slice[1..]; // advance one, as we always stayed < value
    }

    return slice;
}

#[inline(always)]
pub fn gallop_gt<'a, T: Ord>(mut slice: &'a [T], value: &T) -> &'a [T] {
    // if empty slice, or already > element, return
    if slice.len() > 0 && &slice[0] <= value {
        let mut step = 1;
        while step < slice.len() && &slice[step] <= value {
            slice = &slice[step..];
            step = step << 1;
        }

        step = step >> 1;
        while step > 0 {
            if step < slice.len() && &slice[step] <= value {
                slice = &slice[step..];
            }
            step = step >> 1;
        }

        slice = &slice[1..]; // advance one, as we always stayed <= value
    }

    return slice;
}<|endoftext|>


<commit_before>extern crate prust;
use self::prust::*;

fn go() -> Result<(), Error> {
    let platforms = try!(Platform::platforms());
    for p in platforms.iter() {
        println!("Platform: {} version {} from {}.  Extensions: {}",
                 try!(p.name()),
                 try!(p.version()),
                 try!(p.vendor()),
                 try!(p.extensions()));

        let devices = try!(p.devices());
        for d in devices.iter() {
            println!("\tIncludes device: {}.  Max work item sizes: {:?}.  Type: {:?}",
                     try!(d.name()),
                     try!(d.max_work_item_sizes()),
                     try!(d.device_type()));
        }
        let ctx = try!(Context::new(&devices[..]));

        let mut queues: Vec<CommandQueue> = Vec::new();
        for d in devices.iter() {
            match CommandQueue::new(ctx.clone(), d.clone()) {
                Ok(q) => queues.push(q),
                Err(e) => println!("Error creating queue: {:?}", e),
            };
        }

        let program = try!(try!(Program::new_from_source(ctx, &["
        kernel void zero(global int* z) {
            z[get_global_id(0)] *= z[get_global_id(0)];
        }
        "][..])).build(&devices[..]));

        let mut buf = try!(queues[0].create_buffer(1024));
        let mut buf_host = vec!(1,2,3,4,5);

        try!(queues[0].write_buffer(&mut buf, &buf_host[..]));

        let mut k = try!(program.create_kernel("zero"));
        try!(k.bind_mut(0, &mut buf));
        try!(queues[1].run(&mut k, (1,1,1), (2,1,1)));
        try!(queues[0].read_buffer(&buf, &mut buf_host[..]));
        println!("{:?}", buf_host);
    }

    Ok(())
}

pub fn main() {
    match go() {
        Ok(()) => println!(":)"),
        Err(e) => println!("Error: {:?}", e),
    };
}

<commit_msg>Update test.rs<commit_after>extern crate proust;
use self::proust::*;

fn go() -> Result<(), Error> {
    let platforms = try!(Platform::platforms());
    for p in platforms.iter() {
        println!("Platform: {} version {} from {}.  Extensions: {}",
                 try!(p.name()),
                 try!(p.version()),
                 try!(p.vendor()),
                 try!(p.extensions()));

        let devices = try!(p.devices());
        for d in devices.iter() {
            println!("\tIncludes device: {}.  Max work item sizes: {:?}.  Type: {:?}",
                     try!(d.name()),
                     try!(d.max_work_item_sizes()),
                     try!(d.device_type()));
        }
        let ctx = try!(Context::new(&devices[..]));

        let mut queues: Vec<CommandQueue> = Vec::new();
        for d in devices.iter() {
            match CommandQueue::new(ctx.clone(), d.clone()) {
                Ok(q) => queues.push(q),
                Err(e) => println!("Error creating queue: {:?}", e),
            };
        }

        let program = try!(try!(Program::new_from_source(ctx, &["
        kernel void zero(global int* z) {
            z[get_global_id(0)] *= z[get_global_id(0)];
        }
        "][..])).build(&devices[..]));

        let mut buf = try!(queues[0].create_buffer(1024));
        let mut buf_host = vec!(1,2,3,4,5);

        try!(queues[0].write_buffer(&mut buf, &buf_host[..]));

        let mut k = try!(program.create_kernel("zero"));
        try!(k.bind_mut(0, &mut buf));
        try!(queues[1].run(&mut k, (1,1,1), (2,1,1)));
        try!(queues[0].read_buffer(&buf, &mut buf_host[..]));
        println!("{:?}", buf_host);
    }

    Ok(())
}

pub fn main() {
    match go() {
        Ok(()) => println!(":)"),
        Err(e) => println!("Error: {:?}", e),
    };
}

<|endoftext|>


<commit_before>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * 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.
 */

use std::collections::VecDeque;
use std::mem;
use std::net::SocketAddr;

use futures::{
    future, future::Either, sync::{mpsc, oneshot}, Async, AsyncSink, Future, Poll, Sink, Stream,
};

use tokio_executor::{DefaultExecutor, Executor};

use super::connect::{connect, RespConnection};
use error;
use resp;

enum SendStatus {
    Ok,
    End,
    Full(resp::RespValue, bool),
}

impl SendStatus {
    fn full(msg: resp::RespValue) -> Self {
        SendStatus::Full(msg, false)
    }
}

enum FlushStatus {
    Ok,
    Required,
}

enum ReceiveStatus {
    ReadyFinished,
    ReadyMore,
    NotReady,
}

struct PairedConnectionInner {
    connection: RespConnection,
    out_rx: mpsc::UnboundedReceiver<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
    waiting: VecDeque<oneshot::Sender<resp::RespValue>>,

    send_status: SendStatus,
    flush_status: FlushStatus,
}

impl PairedConnectionInner {
    fn new(
        con: RespConnection,
        out_rx: mpsc::UnboundedReceiver<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
    ) -> Self {
        PairedConnectionInner {
            connection: con,
            out_rx: out_rx,
            waiting: VecDeque::new(),
            send_status: SendStatus::Ok,
            flush_status: FlushStatus::Ok,
        }
    }

    fn impl_start_send(&mut self, msg: resp::RespValue) -> Result<bool, ()> {
        match self.connection
            .start_send(msg)
            .map_err(|e| error!("Error sending message to connection: {}", e))?
        {
            AsyncSink::Ready => {
                self.send_status = SendStatus::Ok;
                self.flush_status = FlushStatus::Required;
                Ok(true)
            }
            AsyncSink::NotReady(msg) => {
                self.send_status = SendStatus::full(msg);
                self.flush_status = FlushStatus::Required;
                Ok(false)
            }
        }
    }

    fn poll_start_send(&mut self) -> Result<bool, ()> {
        let message = match self.send_status {
            SendStatus::End | SendStatus::Full(_, false) => return Ok(false),
            SendStatus::Full(ref mut msg_rf, true) => unsafe {
                mem::replace(msg_rf, mem::uninitialized())
            },
            SendStatus::Ok => match self.out_rx
                .poll()
                .map_err(|_| error!("Error polling for messages to send"))?
            {
                Async::Ready(Some((msg, tx))) => {
                    self.waiting.push_back(tx);
                    msg
                }
                Async::Ready(None) => {
                    self.send_status = SendStatus::End;
                    return Ok(false);
                }
                Async::NotReady => return Ok(false),
            },
        };

        self.impl_start_send(message)
    }

    fn poll_complete(&mut self) -> Result<(), ()> {
        match self.flush_status {
            FlushStatus::Ok => (),
            FlushStatus::Required => {
                match self.connection
                    .poll_complete()
                    .map_err(|e| error!("Error polling for completeness: {}", e))?
                {
                    Async::Ready(()) => self.flush_status = FlushStatus::Ok,
                    Async::NotReady => (),
                }
                if let SendStatus::Full(_, ref mut post) = self.send_status {
                    if *post == false {
                        *post = true;
                    }
                }
            }
        }
        Ok(())
    }

    fn receive(&mut self) -> Result<ReceiveStatus, ()> {
        match self.connection
            .poll()
            .map_err(|e| error!("Error polling to receive messages: {}", e))?
        {
            Async::Ready(None) => Ok(ReceiveStatus::ReadyFinished),
            Async::Ready(Some(msg)) => {
                let tx = self.waiting
                    .pop_front()
                    .expect(&format!("Received unexpected message: {:?}", msg));
                let _ = tx.send(msg);
                if let SendStatus::End = self.send_status {
                    if self.waiting.is_empty() {
                        return Ok(ReceiveStatus::ReadyFinished);
                    }
                }
                Ok(ReceiveStatus::ReadyMore)
            }
            Async::NotReady => Ok(ReceiveStatus::NotReady),
        }
    }
}

impl Future for PairedConnectionInner {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        // If there's something to send, send it...
        let mut sending = true;
        while sending {
            sending = self.poll_start_send()?;
        }

        self.poll_complete()?;

        // If there's something to receive, receive it...
        let mut receiving = true;
        while receiving {
            receiving = match self.receive()? {
                ReceiveStatus::NotReady => false,
                ReceiveStatus::ReadyMore => true,
                ReceiveStatus::ReadyFinished => return Ok(Async::Ready(())),
            }
        }

        Ok(Async::NotReady)
    }
}

/// A shareable and cheaply cloneable connection to which Redis commands can be sent
#[derive(Clone)]
pub struct PairedConnection {
    out_tx: mpsc::UnboundedSender<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
}

/// The default starting point to use most default Redis functionality.
///
/// Returns a future that resolves to a `PairedConnection`.
pub fn paired_connect(
    addr: &SocketAddr,
) -> impl Future<Item = PairedConnection, Error = error::Error> + Send {
    connect(addr).map_err(|e| e.into()).map(|connection| {
        let (out_tx, out_rx) = mpsc::unbounded();
        let paired_connection_inner = Box::new(PairedConnectionInner::new(connection, out_rx));
        let mut executor = DefaultExecutor::current();
        executor
            .spawn(paired_connection_inner)
            .expect("Cannot spawn paired connection");
        PairedConnection { out_tx }
    })
}

impl PairedConnection {
    /// Sends a command to Redis.
    ///
    /// The message must be in the format of a single RESP message, this can be constructed
    /// manually or with the `resp_array!` macro.  Returned is a future that resolves to the value
    /// returned from Redis.  The type must be one for which the `resp::FromResp` trait is defined.
    ///
    /// The future will fail for numerous reasons, including but not limited to: IO issues, conversion
    /// problems, and server-side errors being returned by Redis.
    ///
    /// Behind the scenes the message is queued up and sent to Redis asynchronously before the
    /// future is realised.  As such, it is guaranteed that messages are sent in the same order
    /// that `send` is called.
    pub fn send<T: resp::FromResp + Send + 'static>(
        &self,
        msg: resp::RespValue,
    ) -> impl Future<Item = T, Error = error::Error> + Send {
        match &msg {
            &resp::RespValue::Array(_) => (),
            _ => {
                return Either::A(future::err(error::internal(
                    "Command must be a RespValue::Array",
                )))
            }
        }

        let (tx, rx) = oneshot::channel();
        self.out_tx
            .unbounded_send((msg, tx))
            .expect("Cannot send message!");

        Either::B(rx.then(|v| match v {
            Ok(v) => future::result(T::from_resp(v)),
            Err(e) => future::err(e.into()),
        }))
    }

    pub fn send_and_forget(&self, msg: resp::RespValue) {
        let _ = self.send::<resp::RespValue>(msg);
    }
}
<commit_msg>Prevent rare deadlock preventing clean shutdown of connections<commit_after>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * 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.
 */

use std::collections::VecDeque;
use std::mem;
use std::net::SocketAddr;

use futures::{
    future, future::Either, sync::{mpsc, oneshot}, Async, AsyncSink, Future, Poll, Sink, Stream,
};

use tokio_executor::{DefaultExecutor, Executor};

use super::connect::{connect, RespConnection};
use error;
use resp;

enum SendStatus {
    Ok,
    End,
    Full(resp::RespValue, bool),
}

impl SendStatus {
    fn full(msg: resp::RespValue) -> Self {
        SendStatus::Full(msg, false)
    }
}

enum FlushStatus {
    Ok,
    Required,
}

#[derive(Debug)]
enum ReceiveStatus {
    ReadyFinished,
    ReadyMore,
    NotReady,
}

struct PairedConnectionInner {
    connection: RespConnection,
    out_rx: mpsc::UnboundedReceiver<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
    waiting: VecDeque<oneshot::Sender<resp::RespValue>>,

    send_status: SendStatus,
    flush_status: FlushStatus,
}

impl PairedConnectionInner {
    fn new(
        con: RespConnection,
        out_rx: mpsc::UnboundedReceiver<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
    ) -> Self {
        PairedConnectionInner {
            connection: con,
            out_rx: out_rx,
            waiting: VecDeque::new(),
            send_status: SendStatus::Ok,
            flush_status: FlushStatus::Ok,
        }
    }

    fn impl_start_send(&mut self, msg: resp::RespValue) -> Result<bool, ()> {
        match self
            .connection
            .start_send(msg)
            .map_err(|e| error!("Error sending message to connection: {}", e))?
        {
            AsyncSink::Ready => {
                self.send_status = SendStatus::Ok;
                self.flush_status = FlushStatus::Required;
                Ok(true)
            }
            AsyncSink::NotReady(msg) => {
                self.send_status = SendStatus::full(msg);
                self.flush_status = FlushStatus::Required;
                Ok(false)
            }
        }
    }

    fn poll_start_send(&mut self) -> Result<bool, ()> {
        let message = match self.send_status {
            SendStatus::End | SendStatus::Full(_, false) => return Ok(false),
            SendStatus::Full(ref mut msg_rf, true) => unsafe {
                mem::replace(msg_rf, mem::uninitialized())
            },
            SendStatus::Ok => match self
                .out_rx
                .poll()
                .map_err(|_| error!("Error polling for messages to send"))?
            {
                Async::Ready(Some((msg, tx))) => {
                    self.waiting.push_back(tx);
                    msg
                }
                Async::Ready(None) => {
                    self.send_status = SendStatus::End;
                    return Ok(false);
                }
                Async::NotReady => return Ok(false),
            },
        };

        self.impl_start_send(message)
    }

    fn poll_complete(&mut self) -> Result<(), ()> {
        match self.flush_status {
            FlushStatus::Ok => (),
            FlushStatus::Required => {
                match self
                    .connection
                    .poll_complete()
                    .map_err(|e| error!("Error polling for completeness: {}", e))?
                {
                    Async::Ready(()) => self.flush_status = FlushStatus::Ok,
                    Async::NotReady => (),
                }
                if let SendStatus::Full(_, ref mut post) = self.send_status {
                    if *post == false {
                        *post = true;
                    }
                }
            }
        }
        Ok(())
    }

    fn receive(&mut self) -> Result<ReceiveStatus, ()> {
        if let SendStatus::End = self.send_status {
            if self.waiting.is_empty() {
                return Ok(ReceiveStatus::ReadyFinished);
            }
        }
        match self
            .connection
            .poll()
            .map_err(|e| error!("Error polling to receive messages: {}", e))?
        {
            Async::Ready(None) => {
                error!("Connection to Redis closed unexpectedly");
                Err(())
            }
            Async::Ready(Some(msg)) => {
                let tx = match self.waiting.pop_front() {
                    Some(tx) => tx,
                    None => panic!("Received unexpected message: {:?}", msg),
                };
                let _ = tx.send(msg);
                Ok(ReceiveStatus::ReadyMore)
            }
            Async::NotReady => Ok(ReceiveStatus::NotReady),
        }
    }
}

impl Future for PairedConnectionInner {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        // If there's something to send, send it...
        let mut sending = true;
        while sending {
            sending = self.poll_start_send()?;
        }

        self.poll_complete()?;

        // If there's something to receive, receive it...
        loop {
            match self.receive()? {
                ReceiveStatus::NotReady => return Ok(Async::NotReady),
                ReceiveStatus::ReadyMore => (),
                ReceiveStatus::ReadyFinished => return Ok(Async::Ready(())),
            }
        }
    }
}

/// A shareable and cheaply cloneable connection to which Redis commands can be sent
#[derive(Clone)]
pub struct PairedConnection {
    out_tx: mpsc::UnboundedSender<(resp::RespValue, oneshot::Sender<resp::RespValue>)>,
}

/// The default starting point to use most default Redis functionality.
///
/// Returns a future that resolves to a `PairedConnection`.
pub fn paired_connect(
    addr: &SocketAddr,
) -> impl Future<Item = PairedConnection, Error = error::Error> + Send {
    connect(addr).map_err(|e| e.into()).map(|connection| {
        let (out_tx, out_rx) = mpsc::unbounded();
        let paired_connection_inner = Box::new(PairedConnectionInner::new(connection, out_rx));
        let mut executor = DefaultExecutor::current();
        executor
            .spawn(paired_connection_inner)
            .expect("Cannot spawn paired connection");
        PairedConnection { out_tx }
    })
}

impl PairedConnection {
    /// Sends a command to Redis.
    ///
    /// The message must be in the format of a single RESP message, this can be constructed
    /// manually or with the `resp_array!` macro.  Returned is a future that resolves to the value
    /// returned from Redis.  The type must be one for which the `resp::FromResp` trait is defined.
    ///
    /// The future will fail for numerous reasons, including but not limited to: IO issues, conversion
    /// problems, and server-side errors being returned by Redis.
    ///
    /// Behind the scenes the message is queued up and sent to Redis asynchronously before the
    /// future is realised.  As such, it is guaranteed that messages are sent in the same order
    /// that `send` is called.
    pub fn send<T: resp::FromResp + Send + 'static>(
        &self,
        msg: resp::RespValue,
    ) -> impl Future<Item = T, Error = error::Error> {
        match &msg {
            &resp::RespValue::Array(_) => (),
            _ => {
                return Either::A(future::err(error::internal(
                    "Command must be a RespValue::Array",
                )))
            }
        }

        let (tx, rx) = oneshot::channel();
        self.out_tx
            .unbounded_send((msg, tx))
            .expect("Cannot send message!");

        Either::B(rx.then(|v| match v {
            Ok(v) => future::result(T::from_resp(v)),
            Err(e) => future::err(e.into()),
        }))
    }

    pub fn send_and_forget(&self, msg: resp::RespValue) {
        let _ = self.send::<resp::RespValue>(msg);
    }
}
<|endoftext|>


<commit_before>use analysis::imports::Imports;
use analysis::namespaces;
use case::CaseExt;
use codegen::general::{self, cfg_deprecated, derives, version_condition, version_condition_string};
use config::gobjects::GObject;
use env::Env;
use file_saver;
use library::*;
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::Result;
use std::path::Path;
use traits::*;
use version::Version;

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    let configs: Vec<&GObject> = env.config
        .objects
        .values()
        .filter(|c| {
            c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
        })
        .collect();
    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    for config in &configs {
        if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
            has_any = true;
            if get_error_quark_name(enum_).is_some() {
                has_get_quark = true;
            }
            if enum_.glib_get_type.is_some() {
                has_get_type = true;
            }

            if has_get_type && has_get_quark {
                break;
            }
        }
    }

    if !has_any {
        return
    }

    let mut imports = Imports::new(&env.library);
    imports.add("ffi", None);
    if has_get_quark {
        imports.add("glib::Quark", None);
        imports.add("glib::error::ErrorDomain", None);
    }
    if has_get_type {
        imports.add("glib::Type", None);
        imports.add("glib::StaticType", None);
        imports.add("glib::value::Value", None);
        imports.add("glib::value::SetValue", None);
        imports.add("glib::value::FromValue", None);
        imports.add("glib::value::FromValueOptional", None);
        imports.add("gobject_ffi", None);
    }
    imports.add("glib::translate::*", None);

    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        try!(general::start_comments(w, &env.config));
        try!(general::uses(w, env, &imports));
        try!(writeln!(w));

        mod_rs.push("\nmod enums;".into());
        for config in &configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) {
                    mod_rs.push(cfg);
                }
                mod_rs.push(format!("pub use self::enums::{};", enum_.name));
                try!(generate_enum(env, w, enum_, config));
            }
        }

        Ok(())
    });
}

fn generate_enum(env: &Env, w: &mut Write, enum_: &Enumeration, config: &GObject) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        value: String,
        version: Option<Version>,
        deprecated_version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        let ignore = member_config.iter().any(|m| m.ignore);
        if is_alias || ignore || vals.contains(&member.value) {
            continue;
        }
        vals.insert(member.value.clone());
        let deprecated_version = member_config.iter().filter_map(|m| m.deprecated_version).next();
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: member.name.to_camel(),
            c_name: member.c_identifier.clone(),
            value: member.value.clone(),
            version,
            deprecated_version,
        });
    }

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(
        w,
        "#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]"
    ));
    if config.must_use {
        try!(writeln!(
            w,
            "#[must_use]"
        ));
    }
    try!(derives(w, &config.derives, 0));
    try!(writeln!(w, "pub enum {} {{", enum_.name));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 1));
        try!(version_condition(w, env, member.version, false, 1));
        try!(writeln!(w, "\t{},", member.name));
    }
    try!(writeln!(
        w,
        "{}",
        "    #[doc(hidden)]
    __Unknown(i32),
}
"
    ));

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(
        w,
        "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = ffi::{ffi_name};

    fn to_glib(&self) -> ffi::{ffi_name} {{
        match *self {{",
        name = enum_.name,
        ffi_name = enum_.c_type
    ));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(
            w,
            "\t\t\t{}::{} => ffi::{},",
            enum_.name,
            member.name,
            member.c_name
        ));
    }
    try!(writeln!(
        w,
        "\t\t\t{}::__Unknown(value) => value",
        enum_.name
    ));
    try!(writeln!(
        w,
        "{}",
        "        }
    }
}
"
    ));

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(
        w,
        "#[doc(hidden)]
impl FromGlib<ffi::{ffi_name}> for {name} {{
    fn from_glib(value: ffi::{ffi_name}) -> Self {{
        {assert}match value {{",
        name = enum_.name,
        ffi_name = enum_.c_type,
        assert = assert
    ));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(
            w,
            "\t\t\t{} => {}::{},",
            member.value,
            enum_.name,
            member.name
        ));
    }
    try!(writeln!(
        w,
        "\t\t\tvalue => {}::__Unknown(value),",
        enum_.name
    ));
    try!(writeln!(
        w,
        "{}",
        "        }
    }
}
"
    ));
    if let Some(ref get_quark) = get_error_quark_name(enum_) {
        let get_quark = get_quark.replace("-", "_");
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl ErrorDomain for {name} {{
    fn domain() -> Quark {{
        {assert}unsafe {{ from_glib(ffi::{get_quark}()) }}
    }}

    fn code(self) -> i32 {{
        self.to_glib()
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{",
            name = enum_.name,
            get_quark = get_quark,
            assert = assert
        ));

        for member in &members {
            try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
            try!(version_condition(w, env, member.version, false, 3));
            try!(writeln!(
                w,
                "\t\t\t{} => Some({}::{}),",
                member.value,
                enum_.name,
                member.name
            ));
        }
        if has_failed_member {
            try!(writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name));
        } else {
            try!(writeln!(
                w,
                "\t\t\tvalue => Some({}::__Unknown(value)),",
                enum_.name
            ));
        }

        try!(writeln!(
            w,
            "{}",
            "        }
    }
}
"
        ));
    }

    if let Some(ref get_type) = enum_.glib_get_type {
        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl StaticType for {name} {{
    fn static_type() -> Type {{
        unsafe {{ from_glib(ffi::{get_type}()) }}
    }}
}}",
            name = enum_.name,
            get_type = get_type
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl<'a> FromValueOptional<'a> for {name} {{
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl<'a> FromValue<'a> for {name} {{
    unsafe fn from_value(value: &Value) -> Self {{
        from_glib(gobject_ffi::g_value_get_enum(value.to_glib_none().0))
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl SetValue for {name} {{
    unsafe fn set_value(value: &mut Value, this: &Self) {{
        gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));
    }

    Ok(())
}

fn get_error_quark_name(enum_: &Enumeration) -> Option<String> {
    enum_
        .functions
        .iter()
        .find(|f| f.name == "quark")
        .and_then(|f| f.c_identifier.clone())
        .or_else(|| enum_.error_domain.clone())
}
<commit_msg>Derive Ord/PartialOrd impls for enums and only generate all derives if not overridden by config<commit_after>use analysis::imports::Imports;
use analysis::namespaces;
use case::CaseExt;
use codegen::general::{self, cfg_deprecated, derives, version_condition, version_condition_string};
use config::gobjects::GObject;
use env::Env;
use file_saver;
use library::*;
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::Result;
use std::path::Path;
use traits::*;
use version::Version;

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    let configs: Vec<&GObject> = env.config
        .objects
        .values()
        .filter(|c| {
            c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
        })
        .collect();
    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    for config in &configs {
        if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
            has_any = true;
            if get_error_quark_name(enum_).is_some() {
                has_get_quark = true;
            }
            if enum_.glib_get_type.is_some() {
                has_get_type = true;
            }

            if has_get_type && has_get_quark {
                break;
            }
        }
    }

    if !has_any {
        return
    }

    let mut imports = Imports::new(&env.library);
    imports.add("ffi", None);
    if has_get_quark {
        imports.add("glib::Quark", None);
        imports.add("glib::error::ErrorDomain", None);
    }
    if has_get_type {
        imports.add("glib::Type", None);
        imports.add("glib::StaticType", None);
        imports.add("glib::value::Value", None);
        imports.add("glib::value::SetValue", None);
        imports.add("glib::value::FromValue", None);
        imports.add("glib::value::FromValueOptional", None);
        imports.add("gobject_ffi", None);
    }
    imports.add("glib::translate::*", None);

    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        try!(general::start_comments(w, &env.config));
        try!(general::uses(w, env, &imports));
        try!(writeln!(w));

        mod_rs.push("\nmod enums;".into());
        for config in &configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) {
                    mod_rs.push(cfg);
                }
                mod_rs.push(format!("pub use self::enums::{};", enum_.name));
                try!(generate_enum(env, w, enum_, config));
            }
        }

        Ok(())
    });
}

fn generate_enum(env: &Env, w: &mut Write, enum_: &Enumeration, config: &GObject) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        value: String,
        version: Option<Version>,
        deprecated_version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        let ignore = member_config.iter().any(|m| m.ignore);
        if is_alias || ignore || vals.contains(&member.value) {
            continue;
        }
        vals.insert(member.value.clone());
        let deprecated_version = member_config.iter().filter_map(|m| m.deprecated_version).next();
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: member.name.to_camel(),
            c_name: member.c_identifier.clone(),
            value: member.value.clone(),
            version,
            deprecated_version,
        });
    }

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    if config.must_use {
        try!(writeln!(
            w,
            "#[must_use]"
        ));
    }

    if let Some(ref d) = config.derives {
        try!(derives(w, &d, 1));
    } else {
        try!(writeln!(
            w,
            "#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]"
        ));
    }
    try!(writeln!(
            w,
            "#[derive(Clone, Copy)]"
    ));

    try!(writeln!(w, "pub enum {} {{", enum_.name));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 1));
        try!(version_condition(w, env, member.version, false, 1));
        try!(writeln!(w, "\t{},", member.name));
    }
    try!(writeln!(
        w,
        "{}",
        "    #[doc(hidden)]
    __Unknown(i32),
}
"
    ));

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(
        w,
        "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = ffi::{ffi_name};

    fn to_glib(&self) -> ffi::{ffi_name} {{
        match *self {{",
        name = enum_.name,
        ffi_name = enum_.c_type
    ));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(
            w,
            "\t\t\t{}::{} => ffi::{},",
            enum_.name,
            member.name,
            member.c_name
        ));
    }
    try!(writeln!(
        w,
        "\t\t\t{}::__Unknown(value) => value",
        enum_.name
    ));
    try!(writeln!(
        w,
        "{}",
        "        }
    }
}
"
    ));

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(
        w,
        "#[doc(hidden)]
impl FromGlib<ffi::{ffi_name}> for {name} {{
    fn from_glib(value: ffi::{ffi_name}) -> Self {{
        {assert}match value {{",
        name = enum_.name,
        ffi_name = enum_.c_type,
        assert = assert
    ));
    for member in &members {
        try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(
            w,
            "\t\t\t{} => {}::{},",
            member.value,
            enum_.name,
            member.name
        ));
    }
    try!(writeln!(
        w,
        "\t\t\tvalue => {}::__Unknown(value),",
        enum_.name
    ));
    try!(writeln!(
        w,
        "{}",
        "        }
    }
}
"
    ));
    if let Some(ref get_quark) = get_error_quark_name(enum_) {
        let get_quark = get_quark.replace("-", "_");
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl ErrorDomain for {name} {{
    fn domain() -> Quark {{
        {assert}unsafe {{ from_glib(ffi::{get_quark}()) }}
    }}

    fn code(self) -> i32 {{
        self.to_glib()
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{",
            name = enum_.name,
            get_quark = get_quark,
            assert = assert
        ));

        for member in &members {
            try!(cfg_deprecated(w, env, member.deprecated_version, false, 3));
            try!(version_condition(w, env, member.version, false, 3));
            try!(writeln!(
                w,
                "\t\t\t{} => Some({}::{}),",
                member.value,
                enum_.name,
                member.name
            ));
        }
        if has_failed_member {
            try!(writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name));
        } else {
            try!(writeln!(
                w,
                "\t\t\tvalue => Some({}::__Unknown(value)),",
                enum_.name
            ));
        }

        try!(writeln!(
            w,
            "{}",
            "        }
    }
}
"
        ));
    }

    if let Some(ref get_type) = enum_.glib_get_type {
        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl StaticType for {name} {{
    fn static_type() -> Type {{
        unsafe {{ from_glib(ffi::{get_type}()) }}
    }}
}}",
            name = enum_.name,
            get_type = get_type
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl<'a> FromValueOptional<'a> for {name} {{
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl<'a> FromValue<'a> for {name} {{
    unsafe fn from_value(value: &Value) -> Self {{
        from_glib(gobject_ffi::g_value_get_enum(value.to_glib_none().0))
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));

        try!(cfg_deprecated(w, env, enum_.deprecated_version, false, 0));
        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(
            w,
            "impl SetValue for {name} {{
    unsafe fn set_value(value: &mut Value, this: &Self) {{
        gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
    }}
}}",
            name = enum_.name,
        ));
        try!(writeln!(w));
    }

    Ok(())
}

fn get_error_quark_name(enum_: &Enumeration) -> Option<String> {
    enum_
        .functions
        .iter()
        .find(|f| f.name == "quark")
        .and_then(|f| f.c_identifier.clone())
        .or_else(|| enum_.error_domain.clone())
}
<|endoftext|>