<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) PREFIXaa

#[macro_use]
extern crate uucore;

mod platform;

use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::env;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;
use std::{char, fs::remove_file};
use uucore::display::Quotable;
use uucore::parse_size::parse_size;

static OPT_BYTES: &str = "bytes";
static OPT_LINE_BYTES: &str = "line-bytes";
static OPT_LINES: &str = "lines";
static OPT_ADDITIONAL_SUFFIX: &str = "additional-suffix";
static OPT_FILTER: &str = "filter";
static OPT_NUMERIC_SUFFIXES: &str = "numeric-suffixes";
static OPT_SUFFIX_LENGTH: &str = "suffix-length";
static OPT_DEFAULT_SUFFIX_LENGTH: &str = "2";
static OPT_VERBOSE: &str = "verbose";

static ARG_INPUT: &str = "input";
static ARG_PREFIX: &str = "prefix";

fn usage() -> String {
    format!(
        "{0} [OPTION]... [INPUT [PREFIX]]",
        uucore::execution_phrase()
    )
}
fn get_long_usage() -> String {
    format!(
        "Usage:
  {0}

Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
-, read standard input.",
        usage()
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

    let mut settings = Settings {
        prefix: "".to_owned(),
        numeric_suffix: false,
        suffix_length: 0,
        additional_suffix: "".to_owned(),
        input: "".to_owned(),
        filter: None,
        strategy: "".to_owned(),
        strategy_param: "".to_owned(),
        verbose: false,
    };

    settings.suffix_length = matches
        .value_of(OPT_SUFFIX_LENGTH)
        .unwrap()
        .parse()
        .unwrap_or_else(|_| panic!("Invalid number for {}", OPT_SUFFIX_LENGTH));

    settings.numeric_suffix = matches.occurrences_of(OPT_NUMERIC_SUFFIXES) > 0;
    settings.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned();

    settings.verbose = matches.occurrences_of("verbose") > 0;
    // check that the user is not specifying more than one strategy
    // note: right now, this exact behavior cannot be handled by ArgGroup since ArgGroup
    // considers a default value Arg as "defined"
    let explicit_strategies =
        vec![OPT_LINE_BYTES, OPT_LINES, OPT_BYTES]
            .into_iter()
            .fold(0, |count, strategy| {
                if matches.occurrences_of(strategy) > 0 {
                    count + 1
                } else {
                    count
                }
            });
    if explicit_strategies > 1 {
        crash!(1, "cannot split in more than one way");
    }

    // default strategy (if no strategy is passed, use this one)
    settings.strategy = String::from(OPT_LINES);
    settings.strategy_param = matches.value_of(OPT_LINES).unwrap().to_owned();
    // take any (other) defined strategy
    for &strategy in &[OPT_LINE_BYTES, OPT_BYTES] {
        if matches.occurrences_of(strategy) > 0 {
            settings.strategy = String::from(strategy);
            settings.strategy_param = matches.value_of(strategy).unwrap().to_owned();
        }
    }

    settings.input = matches.value_of(ARG_INPUT).unwrap().to_owned();
    settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned();

    if matches.occurrences_of(OPT_FILTER) > 0 {
        if cfg!(windows) {
            // see https://github.com/rust-lang/rust/issues/29494
            show_error!("{} is currently not supported in this platform", OPT_FILTER);
            exit!(-1);
        } else {
            settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned());
        }
    }

    split(&settings)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about("Create output files containing consecutive or interleaved sections of input")
        // strategy (mutually exclusive)
        .arg(
            Arg::with_name(OPT_BYTES)
                .short("b")
                .long(OPT_BYTES)
                .takes_value(true)
                .default_value("2")
                .help("use suffixes of length N (default 2)"),
        )
        .arg(
            Arg::with_name(OPT_LINE_BYTES)
                .short("C")
                .long(OPT_LINE_BYTES)
                .takes_value(true)
                .default_value("2")
                .help("put at most SIZE bytes of lines per output file"),
        )
        .arg(
            Arg::with_name(OPT_LINES)
                .short("l")
                .long(OPT_LINES)
                .takes_value(true)
                .default_value("1000")
                .help("put NUMBER lines/records per output file"),
        )
        // rest of the arguments
        .arg(
            Arg::with_name(OPT_ADDITIONAL_SUFFIX)
                .long(OPT_ADDITIONAL_SUFFIX)
                .takes_value(true)
                .default_value("")
                .help("additional suffix to append to output file names"),
        )
        .arg(
            Arg::with_name(OPT_FILTER)
                .long(OPT_FILTER)
                .takes_value(true)
                .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"),
        )
        .arg(
            Arg::with_name(OPT_NUMERIC_SUFFIXES)
                .short("d")
                .long(OPT_NUMERIC_SUFFIXES)
                .takes_value(true)
                .default_value("0")
                .help("use numeric suffixes instead of alphabetic"),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX_LENGTH)
                .short("a")
                .long(OPT_SUFFIX_LENGTH)
                .takes_value(true)
                .default_value(OPT_DEFAULT_SUFFIX_LENGTH)
                .help("use suffixes of length N (default 2)"),
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
                .long(OPT_VERBOSE)
                .help("print a diagnostic just before each output file is opened"),
        )
        .arg(
            Arg::with_name(ARG_INPUT)
            .takes_value(true)
            .default_value("-")
            .index(1)
        )
        .arg(
            Arg::with_name(ARG_PREFIX)
            .takes_value(true)
            .default_value("x")
            .index(2)
        )
}

#[allow(dead_code)]
struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: usize,
    additional_suffix: String,
    input: String,
    /// When supplied, a shell command to output to instead of xaa, xab …
    filter: Option<String>,
    strategy: String,
    strategy_param: String,
    verbose: bool, // TODO: warning: field is never read: `verbose`
}

trait Splitter {
    // Consume as much as possible from `reader` so as to saturate `writer`.
    // Equivalent to finishing one of the part files. Returns the number of
    // bytes that have been moved.
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128;
}

struct LineSplitter {
    lines_per_split: usize,
}

impl LineSplitter {
    fn new(chunk_size: usize) -> LineSplitter {
        LineSplitter {
            lines_per_split: chunk_size,
        }
    }
}

impl Splitter for LineSplitter {
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128 {
        let mut bytes_consumed = 0u128;
        let mut buffer = String::with_capacity(1024);
        for _ in 0..self.lines_per_split {
            let bytes_read = reader
                .read_line(&mut buffer)
                .unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
            // If we ever read 0 bytes then we know we've hit EOF.
            if bytes_read == 0 {
                return bytes_consumed;
            }

            writer
                .write_all(buffer.as_bytes())
                .unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
            // Empty out the String buffer since `read_line` appends instead of
            // replaces.
            buffer.clear();

            bytes_consumed += bytes_read as u128;
        }

        bytes_consumed
    }
}

struct ByteSplitter {
    bytes_per_split: u128,
}

impl ByteSplitter {
    fn new(chunk_size: usize) -> ByteSplitter {
        ByteSplitter {
            bytes_per_split: u128::try_from(chunk_size).unwrap(),
        }
    }
}

impl Splitter for ByteSplitter {
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128 {
        // We buffer reads and writes. We proceed until `bytes_consumed` is
        // equal to `self.bytes_per_split` or we reach EOF.
        let mut bytes_consumed = 0u128;
        const BUFFER_SIZE: usize = 1024;
        let mut buffer = [0u8; BUFFER_SIZE];
        while bytes_consumed < self.bytes_per_split {
            // Don't overshoot `self.bytes_per_split`! Note: Using std::cmp::min
            // doesn't really work since we have to get types to match which
            // can't be done in a way that keeps all conversions safe.
            let bytes_desired = if (BUFFER_SIZE as u128) <= self.bytes_per_split - bytes_consumed {
                BUFFER_SIZE
            } else {
                // This is a safe conversion since the difference must be less
                // than BUFFER_SIZE in this branch.
                (self.bytes_per_split - bytes_consumed) as usize
            };
            let bytes_read = reader
                .read(&mut buffer[0..bytes_desired])
                .unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
            // If we ever read 0 bytes then we know we've hit EOF.
            if bytes_read == 0 {
                return bytes_consumed;
            }

            writer
                .write_all(&buffer[0..bytes_read])
                .unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));

            bytes_consumed += bytes_read as u128;
        }

        bytes_consumed
    }
}

// (1, 3) -> "aab"
#[allow(clippy::many_single_char_names)]
fn str_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 26usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
#[allow(clippy::many_single_char_names)]
fn num_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 10usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r as u32, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> i32 {
    let mut reader = BufReader::new(if settings.input == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| {
            crash!(
                1,
                "cannot open {} for reading: No such file or directory",
                settings.input.quote()
            )
        });
        Box::new(r) as Box<dyn Read>
    });

    let size_string = &settings.strategy_param;
    let chunk_size = match parse_size(size_string) {
        Ok(n) => n,
        Err(e) => {
            let option_type = if settings.strategy == OPT_LINES {
                "lines"
            } else {
                "bytes"
            };
            crash!(1, "invalid number of {}: {}", option_type, e.to_string())
        }
    };

    let mut splitter: Box<dyn Splitter> = match settings.strategy.as_str() {
        s if s == OPT_LINES => Box::new(LineSplitter::new(chunk_size)),
        s if (s == OPT_BYTES || s == OPT_LINE_BYTES) => Box::new(ByteSplitter::new(chunk_size)),
        a => crash!(1, "strategy {} not supported", a.quote()),
    };

    let mut fileno = 0;
    loop {
        // Get a new part file set up, and construct `writer` for it.
        let mut filename = settings.prefix.clone();
        filename.push_str(
            if settings.numeric_suffix {
                num_prefix(fileno, settings.suffix_length)
            } else {
                str_prefix(fileno, settings.suffix_length)
            }
            .as_ref(),
        );
        filename.push_str(settings.additional_suffix.as_ref());
        let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str());

        let bytes_consumed = splitter.consume(&mut reader, &mut writer);
        writer
            .flush()
            .unwrap_or_else(|e| crash!(1, "error flushing to output file: {}", e));

        // If we didn't write anything we should clean up the empty file, and
        // break from the loop.
        if bytes_consumed == 0 {
            // The output file is only ever created if --filter isn't used.
            // Complicated, I know...
            if settings.filter.is_none() {
                remove_file(filename)
                    .unwrap_or_else(|e| crash!(1, "error removing empty file: {}", e));
            }
            break;
        }

        fileno += 1;
    }
    0
}
<commit_msg>split: create Strategy enum for chunking strategy<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) PREFIXaa

#[macro_use]
extern crate uucore;

mod platform;

use clap::{crate_version, App, Arg, ArgMatches};
use std::convert::TryFrom;
use std::env;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;
use std::{char, fs::remove_file};
use uucore::display::Quotable;
use uucore::parse_size::parse_size;

static OPT_BYTES: &str = "bytes";
static OPT_LINE_BYTES: &str = "line-bytes";
static OPT_LINES: &str = "lines";
static OPT_ADDITIONAL_SUFFIX: &str = "additional-suffix";
static OPT_FILTER: &str = "filter";
static OPT_NUMERIC_SUFFIXES: &str = "numeric-suffixes";
static OPT_SUFFIX_LENGTH: &str = "suffix-length";
static OPT_DEFAULT_SUFFIX_LENGTH: &str = "2";
static OPT_VERBOSE: &str = "verbose";

static ARG_INPUT: &str = "input";
static ARG_PREFIX: &str = "prefix";

fn usage() -> String {
    format!(
        "{0} [OPTION]... [INPUT [PREFIX]]",
        uucore::execution_phrase()
    )
}
fn get_long_usage() -> String {
    format!(
        "Usage:
  {0}

Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
-, read standard input.",
        usage()
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();
    let long_usage = get_long_usage();

    let matches = uu_app()
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .get_matches_from(args);

    let mut settings = Settings {
        prefix: "".to_owned(),
        numeric_suffix: false,
        suffix_length: 0,
        additional_suffix: "".to_owned(),
        input: "".to_owned(),
        filter: None,
        strategy: Strategy::Lines(1000),
        verbose: false,
    };

    settings.suffix_length = matches
        .value_of(OPT_SUFFIX_LENGTH)
        .unwrap()
        .parse()
        .unwrap_or_else(|_| panic!("Invalid number for {}", OPT_SUFFIX_LENGTH));

    settings.numeric_suffix = matches.occurrences_of(OPT_NUMERIC_SUFFIXES) > 0;
    settings.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned();

    settings.verbose = matches.occurrences_of("verbose") > 0;
    settings.strategy = Strategy::from(&matches);
    settings.input = matches.value_of(ARG_INPUT).unwrap().to_owned();
    settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned();

    if matches.occurrences_of(OPT_FILTER) > 0 {
        if cfg!(windows) {
            // see https://github.com/rust-lang/rust/issues/29494
            show_error!("{} is currently not supported in this platform", OPT_FILTER);
            exit!(-1);
        } else {
            settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned());
        }
    }

    split(&settings)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about("Create output files containing consecutive or interleaved sections of input")
        // strategy (mutually exclusive)
        .arg(
            Arg::with_name(OPT_BYTES)
                .short("b")
                .long(OPT_BYTES)
                .takes_value(true)
                .default_value("2")
                .help("use suffixes of length N (default 2)"),
        )
        .arg(
            Arg::with_name(OPT_LINE_BYTES)
                .short("C")
                .long(OPT_LINE_BYTES)
                .takes_value(true)
                .default_value("2")
                .help("put at most SIZE bytes of lines per output file"),
        )
        .arg(
            Arg::with_name(OPT_LINES)
                .short("l")
                .long(OPT_LINES)
                .takes_value(true)
                .default_value("1000")
                .help("put NUMBER lines/records per output file"),
        )
        // rest of the arguments
        .arg(
            Arg::with_name(OPT_ADDITIONAL_SUFFIX)
                .long(OPT_ADDITIONAL_SUFFIX)
                .takes_value(true)
                .default_value("")
                .help("additional suffix to append to output file names"),
        )
        .arg(
            Arg::with_name(OPT_FILTER)
                .long(OPT_FILTER)
                .takes_value(true)
                .help("write to shell COMMAND file name is $FILE (Currently not implemented for Windows)"),
        )
        .arg(
            Arg::with_name(OPT_NUMERIC_SUFFIXES)
                .short("d")
                .long(OPT_NUMERIC_SUFFIXES)
                .takes_value(true)
                .default_value("0")
                .help("use numeric suffixes instead of alphabetic"),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX_LENGTH)
                .short("a")
                .long(OPT_SUFFIX_LENGTH)
                .takes_value(true)
                .default_value(OPT_DEFAULT_SUFFIX_LENGTH)
                .help("use suffixes of length N (default 2)"),
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
                .long(OPT_VERBOSE)
                .help("print a diagnostic just before each output file is opened"),
        )
        .arg(
            Arg::with_name(ARG_INPUT)
            .takes_value(true)
            .default_value("-")
            .index(1)
        )
        .arg(
            Arg::with_name(ARG_PREFIX)
            .takes_value(true)
            .default_value("x")
            .index(2)
        )
}

/// The strategy for breaking up the input file into chunks.
enum Strategy {
    /// Each chunk has the specified number of lines.
    Lines(usize),

    /// Each chunk has the specified number of bytes.
    Bytes(usize),

    /// Each chunk has as many lines as possible without exceeding the
    /// specified number of bytes.
    LineBytes(usize),
}

impl Strategy {
    /// Parse a strategy from the command-line arguments.
    fn from(matches: &ArgMatches) -> Self {
        // Check that the user is not specifying more than one strategy.
        //
        // Note: right now, this exact behavior cannot be handled by
        // `ArgGroup` since `ArgGroup` considers a default value `Arg`
        // as "defined".
        match (
            matches.occurrences_of(OPT_LINES),
            matches.occurrences_of(OPT_BYTES),
            matches.occurrences_of(OPT_LINE_BYTES),
        ) {
            (0, 0, 0) => Strategy::Lines(1000),
            (1, 0, 0) => {
                let s = matches.value_of(OPT_LINES).unwrap();
                let n =
                    parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of lines: {}", e));
                Strategy::Lines(n)
            }
            (0, 1, 0) => {
                let s = matches.value_of(OPT_BYTES).unwrap();
                let n =
                    parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e));
                Strategy::Bytes(n)
            }
            (0, 0, 1) => {
                let s = matches.value_of(OPT_LINE_BYTES).unwrap();
                let n =
                    parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e));
                Strategy::LineBytes(n)
            }
            _ => crash!(1, "cannot split in more than one way"),
        }
    }
}

#[allow(dead_code)]
struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: usize,
    additional_suffix: String,
    input: String,
    /// When supplied, a shell command to output to instead of xaa, xab …
    filter: Option<String>,
    strategy: Strategy,
    verbose: bool, // TODO: warning: field is never read: `verbose`
}

trait Splitter {
    // Consume as much as possible from `reader` so as to saturate `writer`.
    // Equivalent to finishing one of the part files. Returns the number of
    // bytes that have been moved.
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128;
}

struct LineSplitter {
    lines_per_split: usize,
}

impl LineSplitter {
    fn new(chunk_size: usize) -> LineSplitter {
        LineSplitter {
            lines_per_split: chunk_size,
        }
    }
}

impl Splitter for LineSplitter {
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128 {
        let mut bytes_consumed = 0u128;
        let mut buffer = String::with_capacity(1024);
        for _ in 0..self.lines_per_split {
            let bytes_read = reader
                .read_line(&mut buffer)
                .unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
            // If we ever read 0 bytes then we know we've hit EOF.
            if bytes_read == 0 {
                return bytes_consumed;
            }

            writer
                .write_all(buffer.as_bytes())
                .unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
            // Empty out the String buffer since `read_line` appends instead of
            // replaces.
            buffer.clear();

            bytes_consumed += bytes_read as u128;
        }

        bytes_consumed
    }
}

struct ByteSplitter {
    bytes_per_split: u128,
}

impl ByteSplitter {
    fn new(chunk_size: usize) -> ByteSplitter {
        ByteSplitter {
            bytes_per_split: u128::try_from(chunk_size).unwrap(),
        }
    }
}

impl Splitter for ByteSplitter {
    fn consume(
        &mut self,
        reader: &mut BufReader<Box<dyn Read>>,
        writer: &mut BufWriter<Box<dyn Write>>,
    ) -> u128 {
        // We buffer reads and writes. We proceed until `bytes_consumed` is
        // equal to `self.bytes_per_split` or we reach EOF.
        let mut bytes_consumed = 0u128;
        const BUFFER_SIZE: usize = 1024;
        let mut buffer = [0u8; BUFFER_SIZE];
        while bytes_consumed < self.bytes_per_split {
            // Don't overshoot `self.bytes_per_split`! Note: Using std::cmp::min
            // doesn't really work since we have to get types to match which
            // can't be done in a way that keeps all conversions safe.
            let bytes_desired = if (BUFFER_SIZE as u128) <= self.bytes_per_split - bytes_consumed {
                BUFFER_SIZE
            } else {
                // This is a safe conversion since the difference must be less
                // than BUFFER_SIZE in this branch.
                (self.bytes_per_split - bytes_consumed) as usize
            };
            let bytes_read = reader
                .read(&mut buffer[0..bytes_desired])
                .unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
            // If we ever read 0 bytes then we know we've hit EOF.
            if bytes_read == 0 {
                return bytes_consumed;
            }

            writer
                .write_all(&buffer[0..bytes_read])
                .unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));

            bytes_consumed += bytes_read as u128;
        }

        bytes_consumed
    }
}

// (1, 3) -> "aab"
#[allow(clippy::many_single_char_names)]
fn str_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 26usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
#[allow(clippy::many_single_char_names)]
fn num_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 10usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r as u32, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> i32 {
    let mut reader = BufReader::new(if settings.input == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| {
            crash!(
                1,
                "cannot open {} for reading: No such file or directory",
                settings.input.quote()
            )
        });
        Box::new(r) as Box<dyn Read>
    });

    let mut splitter: Box<dyn Splitter> = match settings.strategy {
        Strategy::Lines(chunk_size) => Box::new(LineSplitter::new(chunk_size)),
        Strategy::Bytes(chunk_size) | Strategy::LineBytes(chunk_size) => {
            Box::new(ByteSplitter::new(chunk_size))
        }
    };

    let mut fileno = 0;
    loop {
        // Get a new part file set up, and construct `writer` for it.
        let mut filename = settings.prefix.clone();
        filename.push_str(
            if settings.numeric_suffix {
                num_prefix(fileno, settings.suffix_length)
            } else {
                str_prefix(fileno, settings.suffix_length)
            }
            .as_ref(),
        );
        filename.push_str(settings.additional_suffix.as_ref());
        let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str());

        let bytes_consumed = splitter.consume(&mut reader, &mut writer);
        writer
            .flush()
            .unwrap_or_else(|e| crash!(1, "error flushing to output file: {}", e));

        // If we didn't write anything we should clean up the empty file, and
        // break from the loop.
        if bytes_consumed == 0 {
            // The output file is only ever created if --filter isn't used.
            // Complicated, I know...
            if settings.filter.is_none() {
                remove_file(filename)
                    .unwrap_or_else(|e| crash!(1, "error removing empty file: {}", e));
            }
            break;
        }

        fileno += 1;
    }
    0
}
<|endoftext|>


<commit_before>use crate::*;
use rustc_index::vec::Idx;
use rustc_target::abi::LayoutOf;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn pthread_create(
        &mut self,
        thread: OpTy<'tcx, Tag>,
        _attr: OpTy<'tcx, Tag>,
        start_routine: OpTy<'tcx, Tag>,
        arg: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.tcx.sess.warn(
            "The thread support is experimental. \
             For example, Miri does not detect data races yet.",
        );

        let new_thread_id = this.create_thread()?;
        let old_thread_id = this.set_active_thread(new_thread_id)?;

        let thread_info_place = this.deref_operand(thread)?;
        let thread_info_type = thread.layout.ty
            .builtin_deref(true)
            .ok_or_else(|| err_ub_format!(
                "wrong signature used for `pthread_create`: first argument must be a raw pointer."
            ))?
            .ty;
        let thread_info_layout = this.layout_of(thread_info_type)?;
        this.write_scalar(
            Scalar::from_uint(new_thread_id.index() as u128, thread_info_layout.size),
            thread_info_place.into(),
        )?;

        let fn_ptr = this.read_scalar(start_routine)?.not_undef()?;
        let instance = this.memory.get_fn(fn_ptr)?.as_instance()?;

        let func_arg = match *arg {
            rustc_mir::interpret::Operand::Immediate(immediate) => immediate,
            _ => unreachable!(),
        };
        let func_args = [func_arg];

        let ret_place =
            this.allocate(this.layout_of(this.tcx.types.usize)?, MiriMemoryKind::Machine.into());

        this.call_function(
            instance,
            &func_args[..],
            Some(ret_place.into()),
            StackPopCleanup::None { cleanup: true },
        )?;

        this.set_active_thread(old_thread_id)?;

        Ok(0)
    }

    fn pthread_join(
        &mut self,
        thread: OpTy<'tcx, Tag>,
        retval: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.is_null(this.read_scalar(retval)?.not_undef()?)? {
            throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
        }

        let thread_id = this.read_scalar(thread)?.not_undef()?.to_machine_usize(this)?;
        this.join_thread(thread_id.into())?;

        Ok(0)
    }

    fn pthread_detach(&mut self, thread: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        let thread_id = this.read_scalar(thread)?.not_undef()?.to_machine_usize(this)?;
        this.detach_thread(thread_id.into())?;

        Ok(0)
    }

    fn pthread_self(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        let thread_id = this.get_active_thread()?;
        this.write_scalar(Scalar::from_uint(thread_id.index() as u128, dest.layout.size), dest)
    }

    fn prctl(
        &mut self,
        option: OpTy<'tcx, Tag>,
        arg2: OpTy<'tcx, Tag>,
        _arg3: OpTy<'tcx, Tag>,
        _arg4: OpTy<'tcx, Tag>,
        _arg5: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        let option = this.read_scalar(option)?.not_undef()?.to_i32()?;
        if option != this.eval_libc_i32("PR_SET_NAME")? {
            throw_unsup_format!("Miri supports only PR_SET_NAME");
        }
        let address = this.read_scalar(arg2)?.not_undef()?;
        let name = this.memory.read_c_str(address)?.to_owned();
        this.set_active_thread_name(name)?;

        Ok(0)
    }

    fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.yield_active_thread()?;

        Ok(0)
    }
}
<commit_msg>Change the warning message.<commit_after>use crate::*;
use rustc_index::vec::Idx;
use rustc_target::abi::LayoutOf;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn pthread_create(
        &mut self,
        thread: OpTy<'tcx, Tag>,
        _attr: OpTy<'tcx, Tag>,
        start_routine: OpTy<'tcx, Tag>,
        arg: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.tcx.sess.warn(
            "thread support is experimental. \
             For example, Miri does not detect data races yet.",
        );

        let new_thread_id = this.create_thread()?;
        let old_thread_id = this.set_active_thread(new_thread_id)?;

        let thread_info_place = this.deref_operand(thread)?;
        let thread_info_type = thread.layout.ty
            .builtin_deref(true)
            .ok_or_else(|| err_ub_format!(
                "wrong signature used for `pthread_create`: first argument must be a raw pointer."
            ))?
            .ty;
        let thread_info_layout = this.layout_of(thread_info_type)?;
        this.write_scalar(
            Scalar::from_uint(new_thread_id.index() as u128, thread_info_layout.size),
            thread_info_place.into(),
        )?;

        let fn_ptr = this.read_scalar(start_routine)?.not_undef()?;
        let instance = this.memory.get_fn(fn_ptr)?.as_instance()?;

        let func_arg = match *arg {
            rustc_mir::interpret::Operand::Immediate(immediate) => immediate,
            _ => unreachable!(),
        };
        let func_args = [func_arg];

        let ret_place =
            this.allocate(this.layout_of(this.tcx.types.usize)?, MiriMemoryKind::Machine.into());

        this.call_function(
            instance,
            &func_args[..],
            Some(ret_place.into()),
            StackPopCleanup::None { cleanup: true },
        )?;

        this.set_active_thread(old_thread_id)?;

        Ok(0)
    }

    fn pthread_join(
        &mut self,
        thread: OpTy<'tcx, Tag>,
        retval: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        if !this.is_null(this.read_scalar(retval)?.not_undef()?)? {
            throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
        }

        let thread_id = this.read_scalar(thread)?.not_undef()?.to_machine_usize(this)?;
        this.join_thread(thread_id.into())?;

        Ok(0)
    }

    fn pthread_detach(&mut self, thread: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        let thread_id = this.read_scalar(thread)?.not_undef()?.to_machine_usize(this)?;
        this.detach_thread(thread_id.into())?;

        Ok(0)
    }

    fn pthread_self(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        let thread_id = this.get_active_thread()?;
        this.write_scalar(Scalar::from_uint(thread_id.index() as u128, dest.layout.size), dest)
    }

    fn prctl(
        &mut self,
        option: OpTy<'tcx, Tag>,
        arg2: OpTy<'tcx, Tag>,
        _arg3: OpTy<'tcx, Tag>,
        _arg4: OpTy<'tcx, Tag>,
        _arg5: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        let option = this.read_scalar(option)?.not_undef()?.to_i32()?;
        if option != this.eval_libc_i32("PR_SET_NAME")? {
            throw_unsup_format!("Miri supports only PR_SET_NAME");
        }
        let address = this.read_scalar(arg2)?.not_undef()?;
        let name = this.memory.read_c_str(address)?.to_owned();
        this.set_active_thread_name(name)?;

        Ok(0)
    }

    fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.yield_active_thread()?;

        Ok(0)
    }
}
<|endoftext|>


<commit_before>//! A simple adapter designe solely to print messages on the console.
//!
//! Useful for logging.

use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Value };

use std::collections::HashMap;
use std::sync::Arc;

static ADAPTER_NAME: &'static str = "Console adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct Console {
    setter_stdout_id: Id<Channel>
}

impl Console {
    pub fn id() -> Id<AdapterId> {
        Id::new("console@link.mozilla.org")
    }
    pub fn service_console_id() -> Id<ServiceId> {
        Id::new("service:console@link.mozilla.org")
    }
    pub fn setter_stdout_id() -> Id<Channel> {
        Id::new("setter:stdout@link.mozilla.org")
    }
}
impl Adapter for Console {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, user: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, value)| {
                let result = {
                    if id == self.setter_stdout_id {
                        if let Value::String(s) = value {
                            info!("[console@link.mozilla.org] {} (user {:?})", s, user);
                        } else {
                            info!("[console@link.mozilla.org] {:?} (user {:?})", value, user);
                        }
                        Ok(())
                    } else {
                        Err(Error::InternalError(InternalError::NoSuchChannel(id.clone())))
                    }
                };
                (id, result)
            })
            .collect()
    }
}

impl Console {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let service_console_id = Console::service_console_id();
        let setter_stdout_id = Console::setter_stdout_id();
        let adapter_id = Console::id();
        let console = Arc::new(Console {
            setter_stdout_id: setter_stdout_id.clone()
        });
        try!(adapt.add_adapter(console));
        let mut service = Service::empty(&service_console_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla console v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            kind: ChannelKind::Log,
            supports_send: true,
            ..Channel::empty(&setter_stdout_id, &service_console_id, &adapter_id)
        }));
        Ok(())
    }
}
<commit_msg>Console: Decentralizing ChannelKind<commit_after>//! A simple adapter designe solely to print messages on the console.
//!
//! Useful for logging.

use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Value };

use std::collections::HashMap;
use std::sync::Arc;

static ADAPTER_NAME: &'static str = "Console adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct Console {
    setter_stdout_id: Id<Channel>
}

impl Console {
    pub fn id() -> Id<AdapterId> {
        Id::new("console@link.mozilla.org")
    }
    pub fn service_console_id() -> Id<ServiceId> {
        Id::new("service:console@link.mozilla.org")
    }
    pub fn setter_stdout_id() -> Id<Channel> {
        Id::new("setter:stdout@link.mozilla.org")
    }
}
impl Adapter for Console {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, user: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, value)| {
                let result = {
                    if id == self.setter_stdout_id {
                        if let Value::String(s) = value {
                            info!("[console@link.mozilla.org] {} (user {:?})", s, user);
                        } else {
                            info!("[console@link.mozilla.org] {:?} (user {:?})", value, user);
                        }
                        Ok(())
                    } else {
                        Err(Error::InternalError(InternalError::NoSuchChannel(id.clone())))
                    }
                };
                (id, result)
            })
            .collect()
    }
}

impl Console {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let service_console_id = Console::service_console_id();
        let setter_stdout_id = Console::setter_stdout_id();
        let adapter_id = Console::id();
        let console = Arc::new(Console {
            setter_stdout_id: setter_stdout_id.clone()
        });
        try!(adapt.add_adapter(console));
        let mut service = Service::empty(&service_console_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla console v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            id: setter_stdout_id,
            service: service_console_id,
            adapter: adapter_id,
            ..LOG.clone()
        }));
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use module::Module;
use super::parser::{FileHeaderParser, Parser, ParserError};
use storage::file_id::*;

use regex::Regex;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: String, value_type: Box<FileHeaderSpec> },
    Map { keys: Vec<FileHeaderSpec> },
    Array { allowed_types: Vec<FileHeaderSpec> },
}

#[derive(Debug)]
#[derive(Clone)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Map { keys: Vec<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Map{keys: ref ks} => {
                write!(fmt, "Map -> {:?}", ks)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

impl FileHeaderData {

    pub fn matches_with(&self, r: &Regex) -> bool {
        match self {
            &FileHeaderData::Text(ref t) => r.is_match(&t[..]),
            &FileHeaderData::Key{name: ref n, value: ref val} => {
                r.is_match(n) || val.matches_with(r)
            },

            &FileHeaderData::Map{keys: ref dks} => {
                dks.iter().any(|x| x.matches_with(r))
            },

            &FileHeaderData::Array{values: ref vs} => {
                vs.iter().any(|x| x.matches_with(r))
            }

            _ => false,
        }
    }
}

pub struct MatchError<'a> {
    summary: String,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    debug!("Start matching:\n'{:?}'\non\n{:?}", spec, data);
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            debug!("Matching Key: '{:?}' == '{:?}', Value: '{:?}' == '{:?}'",
                    kname, n,
                    vtype, val);
            if kname != n {
                debug!("Keys not matching");
                unimplemented!();
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Map{keys: ref sks},
            &FileHeaderData::Map{keys: ref dks}
        ) => {
            debug!("Matching Map: '{:?}' == '{:?}'", sks, dks);

            for (s, d) in sks.iter().zip(dks.iter()) {
                let res = match_header_spec(s, d);
                if res.is_some() {
                    return res;
                }
            }
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            debug!("Matching Array: '{:?}' == '{:?}'", vtypes, vs);
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 k, v
                                 ))
        }
    }
    None
}

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File<'a> {
    owning_module   : &'a Module,
    header          : FileHeaderData,
    data            : String,
    id              : FileID,
}

impl<'a> File<'a> {

    pub fn new(module: &'a Module) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object: {:?}", f);
        f
    }

    pub fn from_parser_result(module: &Module, id: FileID, header: FileHeaderData, data: String) -> File {
        let f = File {
            owning_module: module,
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        f
    }

    pub fn new_with_header(module: &Module, h: FileHeaderData) -> File {
        let f = File {
            owning_module: module,
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        f
    }

    pub fn new_with_data(module: &Module, d: String) -> File {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        f
    }

    pub fn new_with_content(module: &Module, h: FileHeaderData, d: String) -> File {
        let f = File {
            owning_module: module,
            header: h,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        f
    }

    pub fn header(&self) -> FileHeaderData {
        self.header.clone()
    }

    pub fn data(&self) -> String {
        self.data.clone()
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    pub fn owner(&self) -> &Module {
        self.owning_module
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        Uuid::new_v4().to_hyphenated_string()
    }
}

impl<'a> Display for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl<'a> Debug for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}
<commit_msg>Add File::matches_with(&Regex)<commit_after>use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use module::Module;
use super::parser::{FileHeaderParser, Parser, ParserError};
use storage::file_id::*;

use regex::Regex;

#[derive(Debug)]
pub enum FileHeaderSpec {
    Null,
    Bool,
    Integer,
    UInteger,
    Float,
    Text,
    Key { name: String, value_type: Box<FileHeaderSpec> },
    Map { keys: Vec<FileHeaderSpec> },
    Array { allowed_types: Vec<FileHeaderSpec> },
}

#[derive(Debug)]
#[derive(Clone)]
pub enum FileHeaderData {
    Null,
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Text(String),
    Key { name: String, value: Box<FileHeaderData> },
    Map { keys: Vec<FileHeaderData> },
    Array { values: Box<Vec<FileHeaderData>> },
}

impl Display for FileHeaderSpec {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        match self {
            &FileHeaderSpec::Null       => write!(fmt, "NULL"),
            &FileHeaderSpec::Bool       => write!(fmt, "Bool"),
            &FileHeaderSpec::Integer    => write!(fmt, "Integer"),
            &FileHeaderSpec::UInteger   => write!(fmt, "UInteger"),
            &FileHeaderSpec::Float      => write!(fmt, "Float"),
            &FileHeaderSpec::Text       => write!(fmt, "Text"),
            &FileHeaderSpec::Key{name: ref n, value_type: ref vt} => {
                write!(fmt, "Key({:?}) -> {:?}", n, vt)
            }
            &FileHeaderSpec::Map{keys: ref ks} => {
                write!(fmt, "Map -> {:?}", ks)
            }
            &FileHeaderSpec::Array{allowed_types: ref at}  => {
                write!(fmt, "Array({:?})", at)
            }
        }
    }

}

impl FileHeaderData {

    pub fn matches_with(&self, r: &Regex) -> bool {
        match self {
            &FileHeaderData::Text(ref t) => r.is_match(&t[..]),
            &FileHeaderData::Key{name: ref n, value: ref val} => {
                r.is_match(n) || val.matches_with(r)
            },

            &FileHeaderData::Map{keys: ref dks} => {
                dks.iter().any(|x| x.matches_with(r))
            },

            &FileHeaderData::Array{values: ref vs} => {
                vs.iter().any(|x| x.matches_with(r))
            }

            _ => false,
        }
    }
}

pub struct MatchError<'a> {
    summary: String,
    expected: &'a FileHeaderSpec,
    found: &'a FileHeaderData
}

impl<'a> MatchError<'a> {

    pub fn new(s: String,
               ex: &'a FileHeaderSpec,
               found: &'a FileHeaderData) -> MatchError<'a> {
        MatchError {
            summary: s,
            expected: ex,
            found: found,
        }
    }

    pub fn format(&self) -> String {
        format!("MatchError: {:?}\nExpected: {:?}\nFound: {:?}\n",
               self.summary, self.expected, self.found)
    }
}

impl<'a> Error for MatchError<'a> {

    fn description(&self) -> &str {
        &self.summary[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl<'a> Debug for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

impl<'a> Display for MatchError<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "{}", self.format());
        Ok(())
    }

}

pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
    -> Option<MatchError<'a>>
{
    debug!("Start matching:\n'{:?}'\non\n{:?}", spec, data);
    match (spec, data) {
        (&FileHeaderSpec::Null,     &FileHeaderData::Null)           => { }
        (&FileHeaderSpec::Bool,     &FileHeaderData::Bool(_))        => { }
        (&FileHeaderSpec::Integer,  &FileHeaderData::Integer(_))     => { }
        (&FileHeaderSpec::UInteger, &FileHeaderData::UInteger(_))    => { }
        (&FileHeaderSpec::Float,    &FileHeaderData::Float(_))       => { }
        (&FileHeaderSpec::Text,     &FileHeaderData::Text(_))        => { }

        (
            &FileHeaderSpec::Key{name: ref kname, value_type: ref vtype},
            &FileHeaderData::Key{name: ref n, value: ref val}
        ) => {
            debug!("Matching Key: '{:?}' == '{:?}', Value: '{:?}' == '{:?}'",
                    kname, n,
                    vtype, val);
            if kname != n {
                debug!("Keys not matching");
                unimplemented!();
            }
            return match_header_spec(&*vtype, &*val);
        }

        (
            &FileHeaderSpec::Map{keys: ref sks},
            &FileHeaderData::Map{keys: ref dks}
        ) => {
            debug!("Matching Map: '{:?}' == '{:?}'", sks, dks);

            for (s, d) in sks.iter().zip(dks.iter()) {
                let res = match_header_spec(s, d);
                if res.is_some() {
                    return res;
                }
            }
        }

        (
            &FileHeaderSpec::Array{allowed_types: ref vtypes},
            &FileHeaderData::Array{values: ref vs}
        ) => {
            debug!("Matching Array: '{:?}' == '{:?}'", vtypes, vs);
            for (t, v) in vtypes.iter().zip(vs.iter()) {
                let res = match_header_spec(t, v);
                if res.is_some() {
                    return res;
                }
            }
        }

        (k, v) => {
            return Some(MatchError::new(String::from("Expected type does not match found type"),
                                 k, v
                                 ))
        }
    }
    None
}

/*
 * Internal abstract view on a file. Does not exist on the FS and is just kept
 * internally until it is written to disk.
 */
pub struct File<'a> {
    owning_module   : &'a Module,
    header          : FileHeaderData,
    data            : String,
    id              : FileID,
}

impl<'a> File<'a> {

    pub fn new(module: &'a Module) -> File<'a> {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object: {:?}", f);
        f
    }

    pub fn from_parser_result(module: &Module, id: FileID, header: FileHeaderData, data: String) -> File {
        let f = File {
            owning_module: module,
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        f
    }

    pub fn new_with_header(module: &Module, h: FileHeaderData) -> File {
        let f = File {
            owning_module: module,
            header: h,
            data: String::from(""),
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        f
    }

    pub fn new_with_data(module: &Module, d: String) -> File {
        let f = File {
            owning_module: module,
            header: FileHeaderData::Null,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        f
    }

    pub fn new_with_content(module: &Module, h: FileHeaderData, d: String) -> File {
        let f = File {
            owning_module: module,
            header: h,
            data: d,
            id: File::get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        f
    }

    pub fn header(&self) -> FileHeaderData {
        self.header.clone()
    }

    pub fn data(&self) -> String {
        self.data.clone()
    }

    pub fn contents(&self) -> (FileHeaderData, String) {
        (self.header(), self.data())
    }

    pub fn id(&self) -> FileID {
        self.id.clone()
    }

    pub fn owner(&self) -> &Module {
        self.owning_module
    }

    pub fn matches_with(&self, r: &Regex) -> bool {
        r.is_match(&self.data[..]) || self.header.matches_with(r)
    }

    fn get_new_file_id() -> FileID {
        use uuid::Uuid;
        Uuid::new_v4().to_hyphenated_string()
    }
}

impl<'a> Display for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}

impl<'a> Debug for File<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt,
"[File] Owner : '{:?}'
        FileID: '{:?}'
        Header: '{:?}'
        Data  : '{:?}'",
               self.owning_module,
               self.header,
               self.data,
               self.id);
        Ok(())
    }

}
<|endoftext|>


<commit_before>
use std::ops::Range;

use assembler::u12;
use assembler::u12::*;

#[derive(PartialEq, Eq, Debug)]
pub struct DataRange {
  /// The first address populated by the bytes in the range.
  start_address: U12,
  /// The bytes occupying the range.
  data: Vec<u8>
}

impl DataRange {

  /**
   @param start_address The address of the first element in the data range in the result.
   @return A new data range from the start address with zero elements.
   */
  pub fn new(start_address: U12) -> Self {
    DataRange {
      start_address: start_address,
      data: Vec::new()
    }
  }

  /**
   @return The number of bytes in the data range.
   */
  pub fn len(&self) -> usize {
    self.data.len()
  }

  /**
   @return An immutable reference to the data underlying the range.
   */
  pub fn data(&self) -> &Vec<u8> {
    &self.data
  }

  /**
   @return A half-open range { address | start <= address < end } representing addresses in the range.
   */
  pub fn address_range(&self) -> Range<U12> {
    Range {
      start: self.start_address,
        end: self.start_address.wrapping_add(self.len().as_u12().unwrap())
    }
  }

  /**
   @return The number of bytes remaining to store data in the range.
   */
  pub fn bytes_remaining(&self) -> usize {
    let total_space_in_range = (u12::MAX - self.start_address).as_usize() + 1;
    total_space_in_range - self.len()
  }

  /**
   Appends the slice to the end of the data range. Checks for capacity in advance.
   @param data The bytes to append to the data range.
   */
  pub fn append(&mut self, data: &[u8]) -> Result<(), String> {
    if self.bytes_remaining() >= data.len() {
      self.data.extend_from_slice(data);
      Ok(())
    } else {
      Err(format!("unable to emit data of length {} into range starting at ${:3X}", data.len(), self.start_address.as_usize()))
    }
  }

}

#[cfg(test)]
mod tests {

  use super::*;

  use assembler::u12;
  use assembler::u12::*;

  #[test]
  fn test_empty_ranges() {
    let zero_range = DataRange::new(U12::zero());
    assert_eq!(zero_range.len(), 0);
    assert_eq!(zero_range.bytes_remaining(), 4096);
    assert_eq!(zero_range.address_range(), U12::zero() .. U12::zero());

    let half_address = 0x800.as_u12().unwrap();
    let half_range = DataRange::new(half_address);
    assert_eq!(half_range.len(), 0);
    assert_eq!(half_range.bytes_remaining(), 2048);
    assert_eq!(half_range.address_range(), half_address .. half_address);

    let max_range = DataRange::new(u12::MAX);
    assert_eq!(max_range.len(), 0);
    assert_eq!(max_range.bytes_remaining(), 1);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MAX);
  }

  #[test]
  fn test_append_within_range() {
    let program_space_start = 0x200.as_u12().unwrap();
    let mut program_space = DataRange::new(program_space_start);
    assert_eq!(program_space.len(), 0);
    assert_eq!(program_space.bytes_remaining(), 3584);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start);

    // Append a single byte to the data range.
    let one_byte_slice: &[u8] = &[1];
    assert_eq!(program_space.append(one_byte_slice), Ok(()));
    assert_eq!(program_space.len(), 1);
    assert_eq!(program_space.bytes_remaining(), 3583);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start.wrapping_add(U12::from(1)));

    // Append a single byte to the data range.
    let two_byte_slice: &[u8] = &[2, 3];
    assert_eq!(program_space.append(two_byte_slice), Ok(()));
    assert_eq!(program_space.len(), 3);
    assert_eq!(program_space.bytes_remaining(), 3581);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start.wrapping_add(U12::from(3)));

    // Validate the data was appended.
    assert_eq!(program_space.data(), &vec![1,2,3]);
  }

  #[test]
  fn test_append_out_of_range() {
    let mut max_range = DataRange::new(u12::MAX);
    let one_byte_slice: &[u8] = &[1];

    // Append data into the only slot in the data range.
    assert_eq!(max_range.append(one_byte_slice), Ok(()));
    assert_eq!(max_range.len(), 1);
    assert_eq!(max_range.bytes_remaining(), 0);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MIN);

    // Validate the data was appended.
    assert_eq!(max_range.data(), &vec![1]);

    // Attempt to apppend past the end of the data range.
    assert_eq!(max_range.append(one_byte_slice), Err(format!("unable to emit data of length 1 into range starting at $FFF")));
    assert_eq!(max_range.len(), 1);
    assert_eq!(max_range.bytes_remaining(), 0);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MIN);

    // Validate the data was not appended.
    assert_eq!(max_range.data(), &vec![1]);
  }

}
<commit_msg>Simplify the API around DataRange.<commit_after>
use std::ops::Range;

use assembler::u12;
use assembler::u12::*;

#[derive(PartialEq, Eq, Debug)]
pub struct DataRange {
  /// The first address populated by the bytes in the range.
  start_address: U12,
  /// The bytes occupying the range.
  data: Vec<u8>
}

impl DataRange {

  /**
   @param start_address The address of the first element in the data range in the result.
   @return A new data range from the start address with zero elements.
   */
  pub fn new(start_address: U12) -> Self {
    DataRange {
      start_address: start_address,
      data: Vec::new()
    }
  }

  /**
   @return The number of bytes in the data range.
   */
  pub fn len(&self) -> usize {
    self.data.len()
  }

  /**
   @return An immutable reference to the data underlying the range.
   */
  pub fn data(&self) -> &Vec<u8> {
    &self.data
  }

  /**
   @return A half-open range { address | start <= address < end } representing addresses in the range.
   */
  pub fn address_range(&self) -> Range<U12> {
    Range {
      start: self.start_address,
        end: self.start_address.wrapping_add(self.len().as_u12().unwrap())
    }
  }

  /**
   @return The number of bytes remaining to store data in the range.
   */
  pub fn bytes_remaining(&self) -> usize {
    let total_space_in_range = (u12::MAX - self.start_address).as_usize() + 1;
    total_space_in_range - self.len()
  }

  /**
   Appends the slice to the end of the data range. Checks for capacity in advance.
   @param data The bytes to append to the data range.
   */
  pub fn append(&mut self, data: &[u8]) -> bool {
    if self.bytes_remaining() >= data.len() {
      self.data.extend_from_slice(data);
      true
    } else {
      false
    }
  }

}

#[cfg(test)]
mod tests {

  use super::*;

  use assembler::u12;
  use assembler::u12::*;

  #[test]
  fn test_empty_ranges() {
    let zero_range = DataRange::new(U12::zero());
    assert_eq!(zero_range.len(), 0);
    assert_eq!(zero_range.bytes_remaining(), 4096);
    assert_eq!(zero_range.address_range(), U12::zero() .. U12::zero());

    let half_address = 0x800.as_u12().unwrap();
    let half_range = DataRange::new(half_address);
    assert_eq!(half_range.len(), 0);
    assert_eq!(half_range.bytes_remaining(), 2048);
    assert_eq!(half_range.address_range(), half_address .. half_address);

    let max_range = DataRange::new(u12::MAX);
    assert_eq!(max_range.len(), 0);
    assert_eq!(max_range.bytes_remaining(), 1);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MAX);
  }

  #[test]
  fn test_append_within_range() {
    let program_space_start = 0x200.as_u12().unwrap();
    let mut program_space = DataRange::new(program_space_start);
    assert_eq!(program_space.len(), 0);
    assert_eq!(program_space.bytes_remaining(), 3584);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start);

    // Append a single byte to the data range.
    let one_byte_slice: &[u8] = &[1];
    assert_eq!(program_space.append(one_byte_slice), true);
    assert_eq!(program_space.len(), 1);
    assert_eq!(program_space.bytes_remaining(), 3583);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start.wrapping_add(U12::from(1)));

    // Append a single byte to the data range.
    let two_byte_slice: &[u8] = &[2, 3];
    assert_eq!(program_space.append(two_byte_slice), true);
    assert_eq!(program_space.len(), 3);
    assert_eq!(program_space.bytes_remaining(), 3581);
    assert_eq!(program_space.address_range(), program_space_start .. program_space_start.wrapping_add(U12::from(3)));

    // Validate the data was appended.
    assert_eq!(program_space.data(), &vec![1,2,3]);
  }

  #[test]
  fn test_append_out_of_range() {
    let mut max_range = DataRange::new(u12::MAX);
    let one_byte_slice: &[u8] = &[1];

    // Append data into the only slot in the data range.
    assert_eq!(max_range.append(one_byte_slice), true);
    assert_eq!(max_range.len(), 1);
    assert_eq!(max_range.bytes_remaining(), 0);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MIN);

    // Validate the data was appended.
    assert_eq!(max_range.data(), &vec![1]);

    // Attempt to apppend past the end of the data range.
    assert_eq!(max_range.append(one_byte_slice), false);
    assert_eq!(max_range.len(), 1);
    assert_eq!(max_range.bytes_remaining(), 0);
    assert_eq!(max_range.address_range(), u12::MAX .. u12::MIN);

    // Validate the data was not appended.
    assert_eq!(max_range.data(), &vec![1]);
  }

}
<|endoftext|>


<commit_before>//! Implementation of the [`Session`] trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call [`AutoSession::new`](::backend::session::auto::AutoSession::new).
//! A new session will be opened, if the any available interface is successful and will be closed once the
//! [`AutoSessionNotifier`](::backend::session::auto::AutoSessionNotifier) is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the [`Session`] interface
//! or be passed to other objects that need it to open devices themselves.
//! The [`AutoSession`](::backend::session::auto::AutoSession) is clonable
//! and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the [`LibinputInputBackend`](::backend::libinput::LibinputInputBackend)
//! (its context might be initialized through a [`Session`] via the [`LibinputSessionInterface`](::backend::libinput::LibinputSessionInterface)).
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via [`AutoSession::change_vt`](::backend::session::Session::change_vt))
//! and to automatically enable it again, when the session becomes active again.
//!
//! It is crucial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the [`Libinput`](input::Libinput) context or the [`Device`](::backend::drm::Device).
//!
//! The [`AutoSessionNotifier`](::backend::session::auto::AutoSessionNotifier) is to be inserted into
//! a calloop event source to have its events processed.

#[cfg(feature = "backend_session_logind")]
use super::logind::{self, LogindSession, LogindSessionNotifier};
use super::{
    direct::{self, DirectSession, DirectSessionNotifier},
    AsErrno, Session, Signal as SessionSignal,
};
use crate::signaling::Signaler;
use nix::fcntl::OFlag;
use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc};

use calloop::{EventSource, Poll, Readiness, Token};

/// [`Session`] using the best available interface
#[derive(Debug, Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// [`SessionNotifier`] using the best available interface
#[derive(Debug)]
pub enum AutoSessionNotifier {
    /// Logind session notifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = crate::slog_or_fallback(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(logger, "Could not create any session, possibilities exhausted");
                        None
                    }
                }
            }
        }
    }

    /// Tries to create a new session via the best available interface.
    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = crate::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(logger, "Could not create any session, possibilities exhausted");
                None
            }
        }
    }
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<(), Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.is_active(),
            AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.seat(),
            AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl AutoSessionNotifier {
    /// Get a handle to the Signaler of this session.
    ///
    /// You can use it to listen for signals generated by the session.
    pub fn signaler(&self) -> Signaler<SessionSignal> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.signaler(),
            AutoSessionNotifier::Direct(ref direct) => direct.signaler(),
        }
    }
}

impl EventSource for AutoSessionNotifier {
    type Event = ();
    type Metadata = ();
    type Ret = ();

    fn process_events<F>(&mut self, readiness: Readiness, token: Token, callback: F) -> io::Result<()>
    where
        F: FnMut((), &mut ()),
    {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => s.process_events(readiness, token, callback),
            AutoSessionNotifier::Direct(s) => s.process_events(readiness, token, callback),
        }
    }

    fn register(&mut self, poll: &mut Poll, token: Token) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::register(s, poll, token),
            AutoSessionNotifier::Direct(s) => EventSource::register(s, poll, token),
        }
    }

    fn reregister(&mut self, poll: &mut Poll, token: Token) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::reregister(s, poll, token),
            AutoSessionNotifier::Direct(s) => EventSource::reregister(s, poll, token),
        }
    }

    fn unregister(&mut self, poll: &mut Poll) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::unregister(s, poll),
            AutoSessionNotifier::Direct(s) => EventSource::unregister(s, poll),
        }
    }
}

/// Errors related to auto sessions
#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[cfg(feature = "backend_session_logind")]
    /// Logind session error
    #[error("Logind session error: {0}")]
    Logind(#[from] logind::Error),
    /// Direct session error
    #[error("Direct session error: {0}")]
    Direct(#[from] direct::Error),
    /// Nix error
    #[error("Nix error: {0}")]
    Nix(#[from] nix::Error),
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<commit_msg>replaced usage of slog_or_stdlog with slog_or_fallback<commit_after>//! Implementation of the [`Session`] trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call [`AutoSession::new`](::backend::session::auto::AutoSession::new).
//! A new session will be opened, if the any available interface is successful and will be closed once the
//! [`AutoSessionNotifier`](::backend::session::auto::AutoSessionNotifier) is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the [`Session`] interface
//! or be passed to other objects that need it to open devices themselves.
//! The [`AutoSession`](::backend::session::auto::AutoSession) is clonable
//! and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the [`LibinputInputBackend`](::backend::libinput::LibinputInputBackend)
//! (its context might be initialized through a [`Session`] via the [`LibinputSessionInterface`](::backend::libinput::LibinputSessionInterface)).
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via [`AutoSession::change_vt`](::backend::session::Session::change_vt))
//! and to automatically enable it again, when the session becomes active again.
//!
//! It is crucial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the [`Libinput`](input::Libinput) context or the [`Device`](::backend::drm::Device).
//!
//! The [`AutoSessionNotifier`](::backend::session::auto::AutoSessionNotifier) is to be inserted into
//! a calloop event source to have its events processed.

#[cfg(feature = "backend_session_logind")]
use super::logind::{self, LogindSession, LogindSessionNotifier};
use super::{
    direct::{self, DirectSession, DirectSessionNotifier},
    AsErrno, Session, Signal as SessionSignal,
};
use crate::signaling::Signaler;
use nix::fcntl::OFlag;
use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc};

use calloop::{EventSource, Poll, Readiness, Token};

/// [`Session`] using the best available interface
#[derive(Debug, Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// [`SessionNotifier`] using the best available interface
#[derive(Debug)]
pub enum AutoSessionNotifier {
    /// Logind session notifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = crate::slog_or_fallback(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(logger, "Could not create any session, possibilities exhausted");
                        None
                    }
                }
            }
        }
    }

    /// Tries to create a new session via the best available interface.
    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = crate::slog_or_fallback(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(logger, "Could not create any session, possibilities exhausted");
                None
            }
        }
    }
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<(), Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Error> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.is_active(),
            AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSession::Logind(ref logind) => logind.seat(),
            AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl AutoSessionNotifier {
    /// Get a handle to the Signaler of this session.
    ///
    /// You can use it to listen for signals generated by the session.
    pub fn signaler(&self) -> Signaler<SessionSignal> {
        match *self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(ref logind) => logind.signaler(),
            AutoSessionNotifier::Direct(ref direct) => direct.signaler(),
        }
    }
}

impl EventSource for AutoSessionNotifier {
    type Event = ();
    type Metadata = ();
    type Ret = ();

    fn process_events<F>(&mut self, readiness: Readiness, token: Token, callback: F) -> io::Result<()>
    where
        F: FnMut((), &mut ()),
    {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => s.process_events(readiness, token, callback),
            AutoSessionNotifier::Direct(s) => s.process_events(readiness, token, callback),
        }
    }

    fn register(&mut self, poll: &mut Poll, token: Token) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::register(s, poll, token),
            AutoSessionNotifier::Direct(s) => EventSource::register(s, poll, token),
        }
    }

    fn reregister(&mut self, poll: &mut Poll, token: Token) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::reregister(s, poll, token),
            AutoSessionNotifier::Direct(s) => EventSource::reregister(s, poll, token),
        }
    }

    fn unregister(&mut self, poll: &mut Poll) -> io::Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            AutoSessionNotifier::Logind(s) => EventSource::unregister(s, poll),
            AutoSessionNotifier::Direct(s) => EventSource::unregister(s, poll),
        }
    }
}

/// Errors related to auto sessions
#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[cfg(feature = "backend_session_logind")]
    /// Logind session error
    #[error("Logind session error: {0}")]
    Logind(#[from] logind::Error),
    /// Direct session error
    #[error("Direct session error: {0}")]
    Direct(#[from] direct::Error),
    /// Nix error
    #[error("Nix error: {0}")]
    Nix(#[from] nix::Error),
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<|endoftext|>


<commit_before>extern crate file;
extern crate tempdir;
extern crate difference;

use std::process;
use std::env;
use std::path::{PathBuf, Path};

use tempdir::TempDir;

fn generator_path() -> PathBuf {
    let test_exe = env::current_exe().unwrap();
    test_exe.parent().unwrap().parent().unwrap().join("gen")
}

fn check_by_path<T: AsRef<Path>>(grammar: T) {
    let grammar = grammar.as_ref();
    let file_name = grammar.display().to_string();
    let input = file::get_text(grammar).unwrap();
    let expected = file::get_text(grammar.with_extension("rs")).unwrap();
    do_test(&input, &expected, &file_name);
}

fn do_test(grammar: &str, expected: &str, file_name: &str) {
    let dir = TempDir::new("gen-tests").unwrap();
    let grammar_path = dir.path().join("weird.fall");
    file::put_text(&grammar_path, grammar).unwrap();

    let output = process::Command::new(generator_path())
        .arg(&grammar_path)
        .output()
        .expect("Failed to execute process");

    if !output.status.success() {
        panic!("Generator exited with code {:?}\n----\n{}\n----\n",
               output.status.code(), std::str::from_utf8(&output.stderr).unwrap())
    }

    let actual = file::get_text(grammar_path.with_extension("rs")).unwrap();

    let expected = expected.trim();
    let actual = actual.trim();
    if expected != actual {
        let difference = difference::Changeset::new(expected, actual, "\n");
        println!("{}\n{}", file_name, difference);
        panic!("Mismatch!")
    }
}

#[test]
fn test_grammars_are_fresh() {
    check_by_path("../fall_test/src/sexp.fall");
    check_by_path("../fall_test/src/rust.fall");
    check_by_path("../fall_test/src/weird.fall");
    check_by_path("../fall_test/src/json.fall");
    check_by_path("./src/syntax.fall");
}
<commit_msg>Simplify test<commit_after>extern crate file;
extern crate tempdir;
extern crate difference;

use std::process;
use std::env;
use std::path::{PathBuf, Path};

use tempdir::TempDir;

const OVERWRITE: bool = true;

fn generator_path() -> PathBuf {
    let test_exe = env::current_exe().unwrap();
    test_exe.parent().unwrap().parent().unwrap().join("gen")
}

fn check_by_path<T: AsRef<Path>>(grammar_path: T) {
    let grammar_path = grammar_path.as_ref();
    let generated_path = &grammar_path.with_extension("rs");
    let grammar_text = file::get_text(grammar_path).unwrap();

    let expected = file::get_text(generated_path).unwrap();

    let generated = {
        let dir = TempDir::new("gen-tests").unwrap();
        let tmp_file = dir.path().join("grammar.fall");
        file::put_text(&tmp_file, grammar_text).unwrap();

        let output = process::Command::new(generator_path())
            .arg(&tmp_file)
            .output()
            .expect("Failed to execute process");

        if !output.status.success() {
            panic!("Generator exited with code {:?}\n----\n{}\n----\n",
                   output.status.code(), std::str::from_utf8(&output.stderr).unwrap())
        }
        file::get_text(tmp_file.with_extension("rs")).unwrap()
    };

    if expected != generated {
        if OVERWRITE {
            println!("UPDATING {}", grammar_path.display());
            file::put_text(generated_path, generated)
                .unwrap_or_else(|_| panic!("Failed to write result to {}", generated_path.display()));
            return
        }
        let difference = difference::Changeset::new(&expected, &generated, "\n");
        println!("MISMATCH {}\n{}", grammar_path.display(), difference);
        panic!("Mismatch!")
    }
}

#[test]
fn test_grammars_are_fresh() {
    check_by_path("../fall_test/src/sexp.fall");
    check_by_path("../fall_test/src/rust.fall");
    check_by_path("../fall_test/src/weird.fall");
    check_by_path("../fall_test/src/json.fall");

    check_by_path("./src/syntax.fall")
}
<|endoftext|>