<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use chrono::{
    prelude::{SecondsFormat, Utc},
    DateTime,
};
use cli::{
    client_proxy::ClientProxy,
    commands::{get_commands, parse_cmd, report_error, Command},
};
use libra_types::{chain_id::ChainId, waypoint::Waypoint};
use rustyline::{config::CompletionType, error::ReadlineError, Config, Editor};
use std::{
    str::FromStr,
    time::{Duration, UNIX_EPOCH},
};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(
    name = "Libra Client",
    author = "The Libra Association",
    about = "Libra client to connect to a specific validator"
)]
struct Args {
    /// Chain ID of the network this client is connecting to
    #[structopt(
        short = "c",
        long,
        help = "\
            Explicitly specify the chain ID of the network the CLI is connecting to: e.g.,
            for mainnet: \"MAINNET\" or 1, testnet: \"TESTNET\" or 2, devnet: \"DEVNET\" or 3, \
            local swarm: \"TESTING\" or 4
            Note: Chain ID of 0 is not allowed
        "
    )]
    pub chain_id: ChainId,
    /// Full URL address to connect to - should include port number, if applicable
    #[structopt(short = "u", long)]
    pub url: String,
    /// Path to the generated keypair for the faucet account. The faucet account can be used to
    /// mint coins. If not passed, a new keypair will be generated for
    /// you and placed in a temporary directory.
    /// To manually generate a keypair, use generate-key:
    /// `cargo run -p generate-keypair -- -o <output_file_path>`
    #[structopt(short = "m", long = "faucet-key-file-path")]
    pub faucet_account_file: Option<String>,
    /// Host that operates a faucet service
    /// If not passed, will be derived from host parameter
    #[structopt(short = "f", long)]
    pub faucet_url: Option<String>,
    /// File location from which to load mnemonic word for user account address/key generation.
    /// If not passed, a new mnemonic file will be generated by libra-wallet in the current
    /// directory.
    #[structopt(short = "n", long)]
    pub mnemonic_file: Option<String>,
    /// If set, client will sync with validator during wallet recovery.
    #[structopt(short = "r", long = "sync")]
    pub sync: bool,
    /// If set, a client uses the waypoint parameter for its initial LedgerInfo verification.
    #[structopt(
        name = "waypoint",
        long,
        help = "Explicitly specify the waypoint to use"
    )]
    pub waypoint: Option<Waypoint>,
    #[structopt(
        name = "waypoint_url",
        long,
        help = "URL for a file with the waypoint to use"
    )]
    pub waypoint_url: Option<String>,
    /// Verbose output.
    #[structopt(short = "v", long = "verbose")]
    pub verbose: bool,
}

fn main() {
    ::libra_logger::Logger::new().init();
    crash_handler::setup_panic_handler();
    let args = Args::from_args();

    let mut logger = ::libra_logger::Logger::new();

    if !args.verbose {
        logger.level(::libra_logger::Level::Warn);
    }
    logger.init();

    let (commands, alias_to_cmd) = get_commands(args.faucet_account_file.is_some());

    let faucet_account_file = args
        .faucet_account_file
        .clone()
        .unwrap_or_else(|| "".to_string());
    // Faucet, TreasuryCompliance and DD use the same keypair for now
    let treasury_compliance_account_file = faucet_account_file.clone();
    let dd_account_file = faucet_account_file.clone();
    let mnemonic_file = args.mnemonic_file.clone();

    // If waypoint is given explicitly, use its value,
    // otherwise waypoint_url is required, try to retrieve the waypoint from the URL.
    let waypoint = args.waypoint.unwrap_or_else(|| {
        args.waypoint_url
            .as_ref()
            .map(|url_str| {
                retrieve_waypoint(url_str.as_str()).unwrap_or_else(|e| {
                    panic!("Failure to retrieve a waypoint from {}: {}", url_str, e)
                })
            })
            .unwrap()
    });
    let mut client_proxy = ClientProxy::new(
        args.chain_id,
        &args.url,
        &faucet_account_file,
        &treasury_compliance_account_file,
        &dd_account_file,
        args.sync,
        args.faucet_url.clone(),
        mnemonic_file,
        waypoint,
    )
    .expect("Failed to construct client.");

    // Test connection to validator
    let block_metadata = client_proxy
        .test_validator_connection()
        .unwrap_or_else(|e| {
            panic!(
                "Not able to connect to validator at {}. Error: {}",
                args.url, e,
            )
        });
    let ledger_info_str = format!(
        "latest version = {}, timestamp = {}",
        block_metadata.version,
        DateTime::<Utc>::from(UNIX_EPOCH + Duration::from_micros(block_metadata.timestamp))
    );
    let cli_info = format!(
        "Connected to validator at: {}, {}",
        args.url, ledger_info_str
    );
    if args.mnemonic_file.is_some() {
        match client_proxy.recover_accounts_in_wallet() {
            Ok(account_data) => {
                println!(
                    "Wallet recovered and the first {} child accounts were derived",
                    account_data.len()
                );
                for data in account_data {
                    println!("#{} address {}", data.index, hex::encode(data.address));
                }
            }
            Err(e) => report_error("Error recovering Libra wallet", e),
        }
    }
    print_help(&cli_info, &commands);
    println!("Please, input commands: \n");

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .auto_add_history(true)
        .build();
    let mut rl = Editor::<()>::with_config(config);
    loop {
        let readline = rl.readline("libra% ");
        match readline {
            Ok(line) => {
                let params = parse_cmd(&line);
                if params.is_empty() {
                    continue;
                }
                match alias_to_cmd.get(&params[0]) {
                    Some(cmd) => {
                        if args.verbose {
                            println!("{}", Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true));
                        }
                        cmd.execute(&mut client_proxy, &params);
                    }
                    None => match params[0] {
                        "quit" | "q!" => break,
                        "help" | "h" => print_help(&cli_info, &commands),
                        "" => continue,
                        x => println!("Unknown command: {:?}", x),
                    },
                }
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

/// Print the help message for the client and underlying command.
fn print_help(client_info: &str, commands: &[std::sync::Arc<dyn Command>]) {
    println!("{}", client_info);
    println!("usage: <command> <args>\n\nUse the following commands:\n");
    for cmd in commands {
        println!(
            "{} {}\n\t{}",
            cmd.get_aliases().join(" | "),
            cmd.get_params_help(),
            cmd.get_description()
        );
    }

    println!("help | h \n\tPrints this help");
    println!("quit | q! \n\tExit this client");
    println!("\n");
}

/// Retrieve a waypoint given the URL.
fn retrieve_waypoint(url_str: &str) -> anyhow::Result<Waypoint> {
    let client = reqwest::blocking::ClientBuilder::new().build()?;
    let response = client.get(url_str).send()?;

    Ok(response
        .error_for_status()
        .map_err(|_| anyhow::format_err!("Failed to retrieve waypoint from URL {}", url_str))?
        .text()
        .map(|r| Waypoint::from_str(r.trim()))??)
}
<commit_msg>[cli] fix cli logger<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use chrono::{
    prelude::{SecondsFormat, Utc},
    DateTime,
};
use cli::{
    client_proxy::ClientProxy,
    commands::{get_commands, parse_cmd, report_error, Command},
};
use libra_types::{chain_id::ChainId, waypoint::Waypoint};
use rustyline::{config::CompletionType, error::ReadlineError, Config, Editor};
use std::{
    str::FromStr,
    time::{Duration, UNIX_EPOCH},
};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(
    name = "Libra Client",
    author = "The Libra Association",
    about = "Libra client to connect to a specific validator"
)]
struct Args {
    /// Chain ID of the network this client is connecting to
    #[structopt(
        short = "c",
        long,
        help = "\
            Explicitly specify the chain ID of the network the CLI is connecting to: e.g.,
            for mainnet: \"MAINNET\" or 1, testnet: \"TESTNET\" or 2, devnet: \"DEVNET\" or 3, \
            local swarm: \"TESTING\" or 4
            Note: Chain ID of 0 is not allowed
        "
    )]
    pub chain_id: ChainId,
    /// Full URL address to connect to - should include port number, if applicable
    #[structopt(short = "u", long)]
    pub url: String,
    /// Path to the generated keypair for the faucet account. The faucet account can be used to
    /// mint coins. If not passed, a new keypair will be generated for
    /// you and placed in a temporary directory.
    /// To manually generate a keypair, use generate-key:
    /// `cargo run -p generate-keypair -- -o <output_file_path>`
    #[structopt(short = "m", long = "faucet-key-file-path")]
    pub faucet_account_file: Option<String>,
    /// Host that operates a faucet service
    /// If not passed, will be derived from host parameter
    #[structopt(short = "f", long)]
    pub faucet_url: Option<String>,
    /// File location from which to load mnemonic word for user account address/key generation.
    /// If not passed, a new mnemonic file will be generated by libra-wallet in the current
    /// directory.
    #[structopt(short = "n", long)]
    pub mnemonic_file: Option<String>,
    /// If set, client will sync with validator during wallet recovery.
    #[structopt(short = "r", long = "sync")]
    pub sync: bool,
    /// If set, a client uses the waypoint parameter for its initial LedgerInfo verification.
    #[structopt(
        name = "waypoint",
        long,
        help = "Explicitly specify the waypoint to use"
    )]
    pub waypoint: Option<Waypoint>,
    #[structopt(
        name = "waypoint_url",
        long,
        help = "URL for a file with the waypoint to use"
    )]
    pub waypoint_url: Option<String>,
    /// Verbose output.
    #[structopt(short = "v", long = "verbose")]
    pub verbose: bool,
}

fn main() {
    let args = Args::from_args();

    let mut logger = ::libra_logger::Logger::new();
    if !args.verbose {
        logger.level(::libra_logger::Level::Warn);
    }
    logger.init();
    crash_handler::setup_panic_handler();

    let (commands, alias_to_cmd) = get_commands(args.faucet_account_file.is_some());

    let faucet_account_file = args
        .faucet_account_file
        .clone()
        .unwrap_or_else(|| "".to_string());
    // Faucet, TreasuryCompliance and DD use the same keypair for now
    let treasury_compliance_account_file = faucet_account_file.clone();
    let dd_account_file = faucet_account_file.clone();
    let mnemonic_file = args.mnemonic_file.clone();

    // If waypoint is given explicitly, use its value,
    // otherwise waypoint_url is required, try to retrieve the waypoint from the URL.
    let waypoint = args.waypoint.unwrap_or_else(|| {
        args.waypoint_url
            .as_ref()
            .map(|url_str| {
                retrieve_waypoint(url_str.as_str()).unwrap_or_else(|e| {
                    panic!("Failure to retrieve a waypoint from {}: {}", url_str, e)
                })
            })
            .unwrap()
    });
    let mut client_proxy = ClientProxy::new(
        args.chain_id,
        &args.url,
        &faucet_account_file,
        &treasury_compliance_account_file,
        &dd_account_file,
        args.sync,
        args.faucet_url.clone(),
        mnemonic_file,
        waypoint,
    )
    .expect("Failed to construct client.");

    // Test connection to validator
    let block_metadata = client_proxy
        .test_validator_connection()
        .unwrap_or_else(|e| {
            panic!(
                "Not able to connect to validator at {}. Error: {}",
                args.url, e,
            )
        });
    let ledger_info_str = format!(
        "latest version = {}, timestamp = {}",
        block_metadata.version,
        DateTime::<Utc>::from(UNIX_EPOCH + Duration::from_micros(block_metadata.timestamp))
    );
    let cli_info = format!(
        "Connected to validator at: {}, {}",
        args.url, ledger_info_str
    );
    if args.mnemonic_file.is_some() {
        match client_proxy.recover_accounts_in_wallet() {
            Ok(account_data) => {
                println!(
                    "Wallet recovered and the first {} child accounts were derived",
                    account_data.len()
                );
                for data in account_data {
                    println!("#{} address {}", data.index, hex::encode(data.address));
                }
            }
            Err(e) => report_error("Error recovering Libra wallet", e),
        }
    }
    print_help(&cli_info, &commands);
    println!("Please, input commands: \n");

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .auto_add_history(true)
        .build();
    let mut rl = Editor::<()>::with_config(config);
    loop {
        let readline = rl.readline("libra% ");
        match readline {
            Ok(line) => {
                let params = parse_cmd(&line);
                if params.is_empty() {
                    continue;
                }
                match alias_to_cmd.get(&params[0]) {
                    Some(cmd) => {
                        if args.verbose {
                            println!("{}", Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true));
                        }
                        cmd.execute(&mut client_proxy, &params);
                    }
                    None => match params[0] {
                        "quit" | "q!" => break,
                        "help" | "h" => print_help(&cli_info, &commands),
                        "" => continue,
                        x => println!("Unknown command: {:?}", x),
                    },
                }
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

/// Print the help message for the client and underlying command.
fn print_help(client_info: &str, commands: &[std::sync::Arc<dyn Command>]) {
    println!("{}", client_info);
    println!("usage: <command> <args>\n\nUse the following commands:\n");
    for cmd in commands {
        println!(
            "{} {}\n\t{}",
            cmd.get_aliases().join(" | "),
            cmd.get_params_help(),
            cmd.get_description()
        );
    }

    println!("help | h \n\tPrints this help");
    println!("quit | q! \n\tExit this client");
    println!("\n");
}

/// Retrieve a waypoint given the URL.
fn retrieve_waypoint(url_str: &str) -> anyhow::Result<Waypoint> {
    let client = reqwest::blocking::ClientBuilder::new().build()?;
    let response = client.get(url_str).send()?;

    Ok(response
        .error_for_status()
        .map_err(|_| anyhow::format_err!("Failed to retrieve waypoint from URL {}", url_str))?
        .text()
        .map(|r| Waypoint::from_str(r.trim()))??)
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use ram::chunk::Chunks;
use ram::cell::CellHeader;
use ram::tombstone::Tombstone;

pub mod combine;
pub mod compact;

pub struct Cleaner {
    chunks: Arc<Chunks>,
    stopped: Arc<AtomicBool>,
    segments_compact_per_turn: usize,
    segments_combine_per_turn: usize,
}

// The two-level cleaner
impl Cleaner {
    pub fn new_and_start(chunks: Arc<Chunks>) -> Cleaner {
        debug!("Starting cleaner for {} chunks", chunks.list.len());
        let stop_tag = Arc::new(AtomicBool::new(false));
        let segments_compact_per_turn = chunks.list[0].segs.len() / 10 + 1;
        let segments_combine_per_turn = chunks.list[0].segs.len() / 20 + 2;
        let cleaner = Cleaner {
            chunks: chunks.clone(),
            stopped: stop_tag.clone(),
            segments_compact_per_turn,
            segments_combine_per_turn
        };
        let stop_tag_ref_clone = stop_tag.clone();
        let checks_ref_clone = chunks.clone();
        thread::Builder::new()
            .name("Cleaner sweeper".into())
            .spawn(move || {
                while !stop_tag.load(Ordering::Relaxed) {
                    for chunk in &chunks.list {
                        chunk.apply_dead_entry();
                        chunk.scan_tombstone_survival();
                    }
                    thread::sleep(Duration::from_millis(100));
                }
            });
        thread::Builder::new()
            .name("Cleaner main".into())
            .spawn(move || {
                while !stop_tag_ref_clone.load(Ordering::Relaxed) {
                    for chunk in &checks_ref_clone.list {
                        trace!("Cleaning chunk {}", chunk.id);
                        let mut cleaned_space: usize = 0;
                        {
                            // compact
                            let segments_for_compact = chunk.segs_for_compact_cleaner();
                            if !segments_for_compact.is_empty() {
                                debug!("Chunk {} have {} segments to compact, overflow {}",
                                       chunk.id, segments_for_compact.len(), segments_compact_per_turn);
                                cleaned_space += segments_for_compact.into_iter()
                                    .take(segments_compact_per_turn) // limit max segment to clean per turn
                                    .map(|segment|
                                        compact::CompactCleaner::clean_segment(chunk, &segment))
                                    .sum::<usize>();
                            }
                        }

                        {
                            // combine
                            let segments_candidates_for_combine: Vec<_> = chunk.segs_for_combine_cleaner();
                            let segments_for_combine: Vec<_> = segments_candidates_for_combine.into_iter()
                                    .take(segments_combine_per_turn)
                                    .collect();
                            if !segments_for_combine.is_empty() {
                                debug!("Chunk {} have {} segments to combine, overflow {}",
                                       chunk.id, segments_for_combine.len(), segments_combine_per_turn);
                                cleaned_space += combine::CombinedCleaner::combine_segments(chunk, &segments_for_combine);
                            }
                        }

                        chunk.check_and_archive_segments();
                        chunk.total_space.fetch_sub(cleaned_space, Ordering::Relaxed);
                    }
                    debug!("Cleaner round finished");
                    thread::sleep(Duration::from_millis(100));
                }
                warn!("Cleaner main thread stopped");
            });
        return cleaner;
    }
}<commit_msg>put reset total space before archiving<commit_after>use std::thread;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use ram::chunk::Chunks;
use ram::cell::CellHeader;
use ram::tombstone::Tombstone;

pub mod combine;
pub mod compact;

pub struct Cleaner {
    chunks: Arc<Chunks>,
    stopped: Arc<AtomicBool>,
    segments_compact_per_turn: usize,
    segments_combine_per_turn: usize,
}

// The two-level cleaner
impl Cleaner {
    pub fn new_and_start(chunks: Arc<Chunks>) -> Cleaner {
        debug!("Starting cleaner for {} chunks", chunks.list.len());
        let stop_tag = Arc::new(AtomicBool::new(false));
        let segments_compact_per_turn = chunks.list[0].segs.len() / 10 + 1;
        let segments_combine_per_turn = chunks.list[0].segs.len() / 20 + 2;
        let cleaner = Cleaner {
            chunks: chunks.clone(),
            stopped: stop_tag.clone(),
            segments_compact_per_turn,
            segments_combine_per_turn
        };
        let stop_tag_ref_clone = stop_tag.clone();
        let checks_ref_clone = chunks.clone();
        thread::Builder::new()
            .name("Cleaner sweeper".into())
            .spawn(move || {
                while !stop_tag.load(Ordering::Relaxed) {
                    for chunk in &chunks.list {
                        chunk.apply_dead_entry();
                        chunk.scan_tombstone_survival();
                    }
                    thread::sleep(Duration::from_millis(100));
                }
            });
        thread::Builder::new()
            .name("Cleaner main".into())
            .spawn(move || {
                while !stop_tag_ref_clone.load(Ordering::Relaxed) {
                    for chunk in &checks_ref_clone.list {
                        trace!("Cleaning chunk {}", chunk.id);
                        let mut cleaned_space: usize = 0;
                        {
                            // compact
                            let segments_for_compact = chunk.segs_for_compact_cleaner();
                            if !segments_for_compact.is_empty() {
                                debug!("Chunk {} have {} segments to compact, overflow {}",
                                       chunk.id, segments_for_compact.len(), segments_compact_per_turn);
                                cleaned_space += segments_for_compact.into_iter()
                                    .take(segments_compact_per_turn) // limit max segment to clean per turn
                                    .map(|segment|
                                        compact::CompactCleaner::clean_segment(chunk, &segment))
                                    .sum::<usize>();
                            }
                        }

                        {
                            // combine
                            let segments_candidates_for_combine: Vec<_> = chunk.segs_for_combine_cleaner();
                            let segments_for_combine: Vec<_> = segments_candidates_for_combine.into_iter()
                                    .take(segments_combine_per_turn)
                                    .collect();
                            if !segments_for_combine.is_empty() {
                                debug!("Chunk {} have {} segments to combine, overflow {}",
                                       chunk.id, segments_for_combine.len(), segments_combine_per_turn);
                                cleaned_space += combine::CombinedCleaner::combine_segments(chunk, &segments_for_combine);
                            }
                        }

                        chunk.total_space.fetch_sub(cleaned_space, Ordering::Relaxed);
                        chunk.check_and_archive_segments();
                    }
                    debug!("Cleaner round finished");
                    thread::sleep(Duration::from_millis(100));
                }
                warn!("Cleaner main thread stopped");
            });
        return cleaner;
    }
}<|endoftext|>


<commit_before>use routing::GraphWatcher;
/// Trait that allows for rendering a `RouteGraph`
pub trait Renderer: GraphWatcher {
    fn get_sample(&mut self, idx: u64, slot: u32) -> f32;
    // TODO: use a multidimensional array type for buffer; remove num_slots param
    /// Fill the provided buffer with samples from a specific slot.
    fn fill_buffer(&mut self, buff: &mut [f32], idx: u64, slot: u32) {
        for (buff_idx, sample) in buff.iter_mut().enumerate() {
            let time_idx = idx + buff_idx as u64;
            *sample = self.get_sample(time_idx, slot);
        }
    }
}
<commit_msg>Remove outdated TODO<commit_after>use routing::GraphWatcher;
/// Trait that allows for rendering a `RouteGraph`
pub trait Renderer: GraphWatcher {
    fn get_sample(&mut self, idx: u64, slot: u32) -> f32;
    /// Fill the provided buffer with samples from a specific slot.
    fn fill_buffer(&mut self, buff: &mut [f32], idx: u64, slot: u32) {
        for (buff_idx, sample) in buff.iter_mut().enumerate() {
            let time_idx = idx + buff_idx as u64;
            *sample = self.get_sample(time_idx, slot);
        }
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Sunrin SHIMURA
// Collaborator: Jian Zeng
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempfile tempdir SUFF TMPDIR tmpname

#[macro_use]
extern crate uucore;

use clap::{App, Arg};

use std::env;
use std::iter;
use std::path::{is_separator, PathBuf};

use rand::Rng;
use tempfile::Builder;

static ABOUT: &str = "create a temporary file or directory.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static DEFAULT_TEMPLATE: &str = "tmp.XXXXXXXXXX";

static OPT_DIRECTORY: &str = "directory";
static OPT_DRY_RUN: &str = "dry-run";
static OPT_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir";
static OPT_T: &str = "t";

static ARG_TEMPLATE: &str = "template";

fn get_usage() -> String {
    format!("{0} [OPTION]... [TEMPLATE]", executable!())
}

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

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("Make a directory instead of a file"),
        )
        .arg(
            Arg::with_name(OPT_DRY_RUN)
                .short("u")
                .long(OPT_DRY_RUN)
                .help("do not create anything; merely print a name (unsafe)"),
        )
        .arg(
            Arg::with_name(OPT_QUIET)
                .short("q")
                .long("quiet")
                .help("Fail silently if an error occurs."),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX)
                .long(OPT_SUFFIX)
                .help(
                    "append SUFF to TEMPLATE; SUFF must not contain a path separator. \
                     This option is implied if TEMPLATE does not end with X.",
                )
                .value_name("SUFF"),
        )
        .arg(
            Arg::with_name(OPT_TMPDIR)
                .short("p")
                .long(OPT_TMPDIR)
                .help(
                    "interpret TEMPLATE relative to DIR; if DIR is not specified, use \
                     $TMPDIR if set, else /tmp. With this option, TEMPLATE must not \
                     be an absolute name; unlike with -t, TEMPLATE may contain \
                     slashes, but mktemp creates only the final component",
                )
                .value_name("DIR"),
        )
        .arg(Arg::with_name(OPT_T).short(OPT_T).help(
            "Generate a template (using the supplied prefix and TMPDIR if set) \
             to create a filename template [deprecated]",
        ))
        .arg(
            Arg::with_name(ARG_TEMPLATE)
                .multiple(false)
                .takes_value(true)
                .max_values(1)
                .default_value(DEFAULT_TEMPLATE),
        )
        .get_matches_from(args);

    let template = matches.value_of(ARG_TEMPLATE).unwrap();
    let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default();

    let (template, mut tmpdir) = if matches.is_present(OPT_TMPDIR)
        && !PathBuf::from(tmpdir).is_dir() // if a temp dir is provided, it must be an actual path
        && tmpdir.contains("XXX")
    // If this is a template, it has to contain at least 3 X
        && template == DEFAULT_TEMPLATE
    // That means that clap does not think we provided a template
    {
        // Special case to workaround a limitation of clap when doing
        // mktemp --tmpdir apt-key-gpghome.XXX
        // The behavior should be
        // mktemp --tmpdir $TMPDIR apt-key-gpghome.XX
        // As --tmpdir is empty
        //
        // Fixed in clap 3
        // See https://github.com/clap-rs/clap/pull/1587
        let tmp = env::temp_dir();
        (tmpdir, tmp)
    } else if !matches.is_present(OPT_TMPDIR) {
        let tmp = env::temp_dir();
        (template, tmp)
    } else {
        (template, PathBuf::from(tmpdir))
    };

    let make_dir = matches.is_present(OPT_DIRECTORY);
    let dry_run = matches.is_present(OPT_DRY_RUN);
    let suppress_file_err = matches.is_present(OPT_QUIET);

    let (prefix, rand, suffix) = match parse_template(template) {
        Some((p, r, s)) => match matches.value_of(OPT_SUFFIX) {
            Some(suf) => {
                if s.is_empty() {
                    (p, r, suf)
                } else {
                    crash!(
                        1,
                        "Template should end with 'X' when you specify suffix option."
                    )
                }
            }
            None => (p, r, s),
        },
        None => ("", 0, ""),
    };

    if rand < 3 {
        crash!(1, "Too few 'X's in template")
    }

    if suffix.chars().any(is_separator) {
        crash!(1, "suffix cannot contain any path separators");
    }

    if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
        show_error!(
            "invalid template, ‘{}’; with --tmpdir, it may not be absolute",
            template
        );
        return 1;
    };

    if matches.is_present(OPT_T) {
        tmpdir = env::temp_dir()
    };

    if dry_run {
        dry_exec(tmpdir, prefix, rand, &suffix)
    } else {
        exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err)
    }
}

fn parse_template(temp: &str) -> Option<(&str, usize, &str)> {
    let right = match temp.rfind('X') {
        Some(r) => r + 1,
        None => return None,
    };
    let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
    let prefix = &temp[..left];
    let rand = right - left;
    let suffix = &temp[right..];
    Some((prefix, rand, suffix))
}

pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> i32 {
    let len = prefix.len() + suffix.len() + rand;
    let mut buf = String::with_capacity(len);
    buf.push_str(prefix);
    buf.extend(iter::repeat('X').take(rand));
    buf.push_str(suffix);

    // Randomize.
    unsafe {
        // We guarantee utf8.
        let bytes = &mut buf.as_mut_vec()[prefix.len()..prefix.len() + rand];
        rand::thread_rng().fill(bytes);
        for byte in bytes.iter_mut() {
            *byte = match *byte % 62 {
                v @ 0..=9 => (v + b'0'),
                v @ 10..=35 => (v - 10 + b'a'),
                v @ 36..=61 => (v - 36 + b'A'),
                _ => unreachable!(),
            }
        }
    }
    tmpdir.push(buf);
    println!("{}", tmpdir.display());
    0
}

fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 {
    let res = if make_dir {
        let tmpdir = Builder::new()
            .prefix(prefix)
            .rand_bytes(rand)
            .suffix(suffix)
            .tempdir_in(&dir);

        // `into_path` consumes the TempDir without removing it
        tmpdir.map(|d| d.into_path().to_string_lossy().to_string())
    } else {
        let tmpfile = Builder::new()
            .prefix(prefix)
            .rand_bytes(rand)
            .suffix(suffix)
            .tempfile_in(&dir);

        match tmpfile {
            Ok(f) => {
                // `keep` ensures that the file is not deleted
                match f.keep() {
                    Ok((_, p)) => Ok(p.to_string_lossy().to_string()),
                    Err(e) => {
                        show_error!("'{}': {}", dir.display(), e);
                        return 1;
                    }
                }
            }
            Err(x) => Err(x),
        }
    };

    match res {
        Ok(ref f) => {
            println!("{}", f);
            0
        }
        Err(e) => {
            if !quiet {
                show_error!("{}: {}", e, dir.display());
            }
            1
        }
    }
}
<commit_msg>refactor/mktemp ~ polish spelling (comments, names, and exceptions)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Sunrin SHIMURA
// Collaborator: Jian Zeng
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (paths) GPGHome

#[macro_use]
extern crate uucore;

use clap::{App, Arg};

use std::env;
use std::iter;
use std::path::{is_separator, PathBuf};

use rand::Rng;
use tempfile::Builder;

static ABOUT: &str = "create a temporary file or directory.";
static VERSION: &str = env!("CARGO_PKG_VERSION");

static DEFAULT_TEMPLATE: &str = "tmp.XXXXXXXXXX";

static OPT_DIRECTORY: &str = "directory";
static OPT_DRY_RUN: &str = "dry-run";
static OPT_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir";
static OPT_T: &str = "t";

static ARG_TEMPLATE: &str = "template";

fn get_usage() -> String {
    format!("{0} [OPTION]... [TEMPLATE]", executable!())
}

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

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("Make a directory instead of a file"),
        )
        .arg(
            Arg::with_name(OPT_DRY_RUN)
                .short("u")
                .long(OPT_DRY_RUN)
                .help("do not create anything; merely print a name (unsafe)"),
        )
        .arg(
            Arg::with_name(OPT_QUIET)
                .short("q")
                .long("quiet")
                .help("Fail silently if an error occurs."),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX)
                .long(OPT_SUFFIX)
                .help(
                    "append SUFFIX to TEMPLATE; SUFFIX must not contain a path separator. \
                     This option is implied if TEMPLATE does not end with X.",
                )
                .value_name("SUFFIX"),
        )
        .arg(
            Arg::with_name(OPT_TMPDIR)
                .short("p")
                .long(OPT_TMPDIR)
                .help(
                    "interpret TEMPLATE relative to DIR; if DIR is not specified, use \
                     $TMPDIR if set, else /tmp. With this option, TEMPLATE must not \
                     be an absolute name; unlike with -t, TEMPLATE may contain \
                     slashes, but mktemp creates only the final component",
                )
                .value_name("DIR"),
        )
        .arg(Arg::with_name(OPT_T).short(OPT_T).help(
            "Generate a template (using the supplied prefix and TMPDIR if set) \
             to create a filename template [deprecated]",
        ))
        .arg(
            Arg::with_name(ARG_TEMPLATE)
                .multiple(false)
                .takes_value(true)
                .max_values(1)
                .default_value(DEFAULT_TEMPLATE),
        )
        .get_matches_from(args);

    let template = matches.value_of(ARG_TEMPLATE).unwrap();
    let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default();

    let (template, mut tmpdir) = if matches.is_present(OPT_TMPDIR)
        && !PathBuf::from(tmpdir).is_dir() // if a temp dir is provided, it must be an actual path
        && tmpdir.contains("XXX")
    // If this is a template, it has to contain at least 3 X
        && template == DEFAULT_TEMPLATE
    // That means that clap does not think we provided a template
    {
        // Special case to workaround a limitation of clap when doing
        // mktemp --tmpdir apt-key-gpghome.XXX
        // The behavior should be
        // mktemp --tmpdir $TMPDIR apt-key-gpghome.XX
        // As --tmpdir is empty
        //
        // Fixed in clap 3
        // See https://github.com/clap-rs/clap/pull/1587
        let tmp = env::temp_dir();
        (tmpdir, tmp)
    } else if !matches.is_present(OPT_TMPDIR) {
        let tmp = env::temp_dir();
        (template, tmp)
    } else {
        (template, PathBuf::from(tmpdir))
    };

    let make_dir = matches.is_present(OPT_DIRECTORY);
    let dry_run = matches.is_present(OPT_DRY_RUN);
    let suppress_file_err = matches.is_present(OPT_QUIET);

    let (prefix, rand, suffix) = match parse_template(template) {
        Some((p, r, s)) => match matches.value_of(OPT_SUFFIX) {
            Some(suf) => {
                if s.is_empty() {
                    (p, r, suf)
                } else {
                    crash!(
                        1,
                        "Template should end with 'X' when you specify suffix option."
                    )
                }
            }
            None => (p, r, s),
        },
        None => ("", 0, ""),
    };

    if rand < 3 {
        crash!(1, "Too few 'X's in template")
    }

    if suffix.chars().any(is_separator) {
        crash!(1, "suffix cannot contain any path separators");
    }

    if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
        show_error!(
            "invalid template, ‘{}’; with --tmpdir, it may not be absolute",
            template
        );
        return 1;
    };

    if matches.is_present(OPT_T) {
        tmpdir = env::temp_dir()
    };

    if dry_run {
        dry_exec(tmpdir, prefix, rand, &suffix)
    } else {
        exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err)
    }
}

fn parse_template(temp: &str) -> Option<(&str, usize, &str)> {
    let right = match temp.rfind('X') {
        Some(r) => r + 1,
        None => return None,
    };
    let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
    let prefix = &temp[..left];
    let rand = right - left;
    let suffix = &temp[right..];
    Some((prefix, rand, suffix))
}

pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> i32 {
    let len = prefix.len() + suffix.len() + rand;
    let mut buf = String::with_capacity(len);
    buf.push_str(prefix);
    buf.extend(iter::repeat('X').take(rand));
    buf.push_str(suffix);

    // Randomize.
    unsafe {
        // We guarantee utf8.
        let bytes = &mut buf.as_mut_vec()[prefix.len()..prefix.len() + rand];
        rand::thread_rng().fill(bytes);
        for byte in bytes.iter_mut() {
            *byte = match *byte % 62 {
                v @ 0..=9 => (v + b'0'),
                v @ 10..=35 => (v - 10 + b'a'),
                v @ 36..=61 => (v - 36 + b'A'),
                _ => unreachable!(),
            }
        }
    }
    tmpdir.push(buf);
    println!("{}", tmpdir.display());
    0
}

fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 {
    let res = if make_dir {
        let tmpdir = Builder::new()
            .prefix(prefix)
            .rand_bytes(rand)
            .suffix(suffix)
            .tempdir_in(&dir);

        // `into_path` consumes the TempDir without removing it
        tmpdir.map(|d| d.into_path().to_string_lossy().to_string())
    } else {
        let tmpfile = Builder::new()
            .prefix(prefix)
            .rand_bytes(rand)
            .suffix(suffix)
            .tempfile_in(&dir);

        match tmpfile {
            Ok(f) => {
                // `keep` ensures that the file is not deleted
                match f.keep() {
                    Ok((_, p)) => Ok(p.to_string_lossy().to_string()),
                    Err(e) => {
                        show_error!("'{}': {}", dir.display(), e);
                        return 1;
                    }
                }
            }
            Err(x) => Err(x),
        }
    };

    match res {
        Ok(ref f) => {
            println!("{}", f);
            0
        }
        Err(e) => {
            if !quiet {
                show_error!("{}: {}", e, dir.display());
            }
            1
        }
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Debertol <michael.debertol..AT..gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

//! Sort big files by using auxiliary files for storing intermediate chunks.
//!
//! Files are read into chunks of memory which are then sorted individually and
//! written to temporary files. There are two threads: One sorter, and one reader/writer.
//! The buffers for the individual chunks are recycled. There are two buffers.

use std::cmp::Ordering;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::{
    fs::OpenOptions,
    io::Read,
    sync::mpsc::{Receiver, SyncSender},
    thread,
};

use itertools::Itertools;

use tempfile::TempDir;

use crate::{
    chunks::{self, Chunk},
    compare_by, merge, output_sorted_lines, sort_by, GlobalSettings,
};

const MIN_BUFFER_SIZE: usize = 8_000;

/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
pub fn ext_sort(files: &mut impl Iterator<Item = Box<dyn Read + Send>>, settings: &GlobalSettings) {
    let tmp_dir = crash_if_err!(
        1,
        tempfile::Builder::new()
            .prefix("uutils_sort")
            .tempdir_in(&settings.tmp_dir)
    );
    let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1);
    let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1);
    thread::spawn({
        let settings = settings.clone();
        move || sorter(recycled_receiver, sorted_sender, settings)
    });
    let read_result = reader_writer(
        files,
        &tmp_dir,
        if settings.zero_terminated {
            b'\0'
        } else {
            b'\n'
        },
        // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
        // around settings.buffer_size as a whole.
        settings.buffer_size / 10,
        settings.clone(),
        sorted_receiver,
        recycled_sender,
    );
    match read_result {
        ReadResult::WroteChunksToFile { chunks_written } => {
            let files = (0..chunks_written)
                .map(|chunk_num| tmp_dir.path().join(chunk_num.to_string()))
                .collect::<Vec<_>>();
            let mut merger = merge::merge(&files, settings);
            merger.write_all(settings);
        }
        ReadResult::SortedSingleChunk(chunk) => {
            output_sorted_lines(chunk.borrow_lines().iter(), settings);
        }
        ReadResult::SortedTwoChunks([a, b]) => {
            let merged_iter = a
                .borrow_lines()
                .iter()
                .merge_by(b.borrow_lines().iter(), |line_a, line_b| {
                    compare_by(line_a, line_b, settings) != Ordering::Greater
                });
            output_sorted_lines(merged_iter, settings);
        }
        ReadResult::EmptyInput => {
            // don't output anything
        }
    }
}

/// The function that is executed on the sorter thread.
fn sorter(receiver: Receiver<Chunk>, sender: SyncSender<Chunk>, settings: GlobalSettings) {
    while let Ok(mut payload) = receiver.recv() {
        payload.with_lines_mut(|lines| sort_by(lines, &settings));
        sender.send(payload).unwrap();
    }
}

/// Describes how we read the chunks from the input.
enum ReadResult {
    /// The input was empty. Nothing was read.
    EmptyInput,
    /// The input fits into a single Chunk, which was kept in memory.
    SortedSingleChunk(Chunk),
    /// The input fits into two chunks, which were kept in memory.
    SortedTwoChunks([Chunk; 2]),
    /// The input was read into multiple chunks, which were written to auxiliary files.
    WroteChunksToFile {
        /// The number of chunks written to auxiliary files.
        chunks_written: usize,
    },
}

/// The function that is executed on the reader/writer thread.
///
/// # Returns
/// * The number of chunks read.
fn reader_writer(
    mut files: impl Iterator<Item = Box<dyn Read + Send>>,
    tmp_dir: &TempDir,
    separator: u8,
    buffer_size: usize,
    settings: GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
) -> ReadResult {
    let mut sender_option = Some(sender);

    let mut file = files.next().unwrap();

    let mut carry_over = vec![];
    // kick things off with two reads
    for _ in 0..2 {
        chunks::read(
            &mut sender_option,
            vec![0; MIN_BUFFER_SIZE],
            Some(buffer_size),
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            Vec::new(),
            &settings,
        );
        if sender_option.is_none() {
            // We have already read the whole input. Since we are in our first two reads,
            // this means that we can fit the whole input into memory. Bypass writing below and
            // handle this case in a more straightforward way.
            return if let Ok(first_chunk) = receiver.recv() {
                if let Ok(second_chunk) = receiver.recv() {
                    ReadResult::SortedTwoChunks([first_chunk, second_chunk])
                } else {
                    ReadResult::SortedSingleChunk(first_chunk)
                }
            } else {
                ReadResult::EmptyInput
            };
        }
    }

    let mut file_number = 0;
    loop {
        let mut chunk = match receiver.recv() {
            Ok(it) => it,
            _ => {
                return ReadResult::WroteChunksToFile {
                    chunks_written: file_number,
                }
            }
        };

        write(
            &mut chunk,
            &tmp_dir.path().join(file_number.to_string()),
            separator,
        );

        file_number += 1;

        let (recycled_lines, recycled_buffer) = chunk.recycle();

        chunks::read(
            &mut sender_option,
            recycled_buffer,
            None,
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            recycled_lines,
            &settings,
        );
    }
}

/// Write the lines in `chunk` to `file`, separated by `separator`.
fn write(chunk: &mut Chunk, file: &Path, separator: u8) {
    chunk.with_lines_mut(|lines| {
        // Write the lines to the file
        let file = crash_if_err!(1, OpenOptions::new().create(true).write(true).open(file));
        let mut writer = BufWriter::new(file);
        for s in lines.iter() {
            crash_if_err!(1, writer.write_all(s.line.as_bytes()));
            crash_if_err!(1, writer.write_all(&[separator]));
        }
    });
}
<commit_msg>sort: never use a bigger buffer than requested<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Debertol <michael.debertol..AT..gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

//! Sort big files by using auxiliary files for storing intermediate chunks.
//!
//! Files are read into chunks of memory which are then sorted individually and
//! written to temporary files. There are two threads: One sorter, and one reader/writer.
//! The buffers for the individual chunks are recycled. There are two buffers.

use std::cmp::Ordering;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::{
    fs::OpenOptions,
    io::Read,
    sync::mpsc::{Receiver, SyncSender},
    thread,
};

use itertools::Itertools;

use tempfile::TempDir;

use crate::{
    chunks::{self, Chunk},
    compare_by, merge, output_sorted_lines, sort_by, GlobalSettings,
};

const START_BUFFER_SIZE: usize = 8_000;

/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
pub fn ext_sort(files: &mut impl Iterator<Item = Box<dyn Read + Send>>, settings: &GlobalSettings) {
    let tmp_dir = crash_if_err!(
        1,
        tempfile::Builder::new()
            .prefix("uutils_sort")
            .tempdir_in(&settings.tmp_dir)
    );
    let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1);
    let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1);
    thread::spawn({
        let settings = settings.clone();
        move || sorter(recycled_receiver, sorted_sender, settings)
    });
    let read_result = reader_writer(
        files,
        &tmp_dir,
        if settings.zero_terminated {
            b'\0'
        } else {
            b'\n'
        },
        // Heuristically chosen: Dividing by 10 seems to keep our memory usage roughly
        // around settings.buffer_size as a whole.
        settings.buffer_size / 10,
        settings.clone(),
        sorted_receiver,
        recycled_sender,
    );
    match read_result {
        ReadResult::WroteChunksToFile { chunks_written } => {
            let files = (0..chunks_written)
                .map(|chunk_num| tmp_dir.path().join(chunk_num.to_string()))
                .collect::<Vec<_>>();
            let mut merger = merge::merge(&files, settings);
            merger.write_all(settings);
        }
        ReadResult::SortedSingleChunk(chunk) => {
            output_sorted_lines(chunk.borrow_lines().iter(), settings);
        }
        ReadResult::SortedTwoChunks([a, b]) => {
            let merged_iter = a
                .borrow_lines()
                .iter()
                .merge_by(b.borrow_lines().iter(), |line_a, line_b| {
                    compare_by(line_a, line_b, settings) != Ordering::Greater
                });
            output_sorted_lines(merged_iter, settings);
        }
        ReadResult::EmptyInput => {
            // don't output anything
        }
    }
}

/// The function that is executed on the sorter thread.
fn sorter(receiver: Receiver<Chunk>, sender: SyncSender<Chunk>, settings: GlobalSettings) {
    while let Ok(mut payload) = receiver.recv() {
        payload.with_lines_mut(|lines| sort_by(lines, &settings));
        sender.send(payload).unwrap();
    }
}

/// Describes how we read the chunks from the input.
enum ReadResult {
    /// The input was empty. Nothing was read.
    EmptyInput,
    /// The input fits into a single Chunk, which was kept in memory.
    SortedSingleChunk(Chunk),
    /// The input fits into two chunks, which were kept in memory.
    SortedTwoChunks([Chunk; 2]),
    /// The input was read into multiple chunks, which were written to auxiliary files.
    WroteChunksToFile {
        /// The number of chunks written to auxiliary files.
        chunks_written: usize,
    },
}

/// The function that is executed on the reader/writer thread.
///
/// # Returns
/// * The number of chunks read.
fn reader_writer(
    mut files: impl Iterator<Item = Box<dyn Read + Send>>,
    tmp_dir: &TempDir,
    separator: u8,
    buffer_size: usize,
    settings: GlobalSettings,
    receiver: Receiver<Chunk>,
    sender: SyncSender<Chunk>,
) -> ReadResult {
    let mut sender_option = Some(sender);

    let mut file = files.next().unwrap();

    let mut carry_over = vec![];
    // kick things off with two reads
    for _ in 0..2 {
        chunks::read(
            &mut sender_option,
            vec![
                0;
                if START_BUFFER_SIZE < buffer_size {
                    START_BUFFER_SIZE
                } else {
                    buffer_size
                }
            ],
            Some(buffer_size),
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            Vec::new(),
            &settings,
        );
        if sender_option.is_none() {
            // We have already read the whole input. Since we are in our first two reads,
            // this means that we can fit the whole input into memory. Bypass writing below and
            // handle this case in a more straightforward way.
            return if let Ok(first_chunk) = receiver.recv() {
                if let Ok(second_chunk) = receiver.recv() {
                    ReadResult::SortedTwoChunks([first_chunk, second_chunk])
                } else {
                    ReadResult::SortedSingleChunk(first_chunk)
                }
            } else {
                ReadResult::EmptyInput
            };
        }
    }

    let mut file_number = 0;
    loop {
        let mut chunk = match receiver.recv() {
            Ok(it) => it,
            _ => {
                return ReadResult::WroteChunksToFile {
                    chunks_written: file_number,
                }
            }
        };

        write(
            &mut chunk,
            &tmp_dir.path().join(file_number.to_string()),
            separator,
        );

        file_number += 1;

        let (recycled_lines, recycled_buffer) = chunk.recycle();

        chunks::read(
            &mut sender_option,
            recycled_buffer,
            None,
            &mut carry_over,
            &mut file,
            &mut files,
            separator,
            recycled_lines,
            &settings,
        );
    }
}

/// Write the lines in `chunk` to `file`, separated by `separator`.
fn write(chunk: &mut Chunk, file: &Path, separator: u8) {
    chunk.with_lines_mut(|lines| {
        // Write the lines to the file
        let file = crash_if_err!(1, OpenOptions::new().create(true).write(true).open(file));
        let mut writer = BufWriter::new(file);
        for s in lines.iter() {
            crash_if_err!(1, writer.write_all(s.line.as_bytes()));
            crash_if_err!(1, writer.write_all(&[separator]));
        }
    });
}
<|endoftext|>


<commit_before>use libc::c_int;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_char, c_uchar, c_uint, c_void, int16_t, uint8_t};
    use joystick::ll::{SDL_Joystick, SDL_JoystickGUID};

    pub type SDL_bool = c_int;

    pub type SDL_GameController = c_void;

    pub type SDL_GameControllerBindType = c_uint;
    pub const SDL_CONTROLLER_BINDTYPE_NONE: SDL_GameControllerBindType = 0;
    pub const SDL_CONTROLLER_BINDTYPE_BUTTON: SDL_GameControllerBindType = 1;
    pub const SDL_CONTROLLER_BINDTYPE_AXIS: SDL_GameControllerBindType = 2;
    pub const SDL_CONTROLLER_BINDTYPE_HAT: SDL_GameControllerBindType = 3;

    #[allow(dead_code, non_snake_case)]
    #[repr(C)]
    pub struct SDL_GameControllerButtonBind {
        bindType: SDL_GameControllerBindType,
        value: SDL_GameControllerButtonBindData,
    }

    #[allow(dead_code)]
    #[repr(C)]
    pub struct SDL_GameControllerButtonBindData {
        data: [c_uchar, ..8u],
    }

    #[allow(dead_code)]
    pub struct SDL_GameControllerButtonBindDataHat {
        hat: c_int,
        hat_mask: c_int,
    }

    impl SDL_GameControllerButtonBindData {
        pub fn button(&self) -> *const c_int {
            self.data.as_ptr() as *const _
        }
        pub fn axis(&self) -> *const c_int {
            self.data.as_ptr() as *const _
        }
        pub fn hat(&self) -> *const SDL_GameControllerButtonBindDataHat {
            self.data.as_ptr() as *const _
        }
    }

    pub type SDL_GameControllerAxis = c_int;
    pub const SDL_CONTROLLER_AXIS_INVALID: SDL_GameControllerAxis = -1;
    pub const SDL_CONTROLLER_AXIS_LEFTX: SDL_GameControllerAxis = 0;
    pub const SDL_CONTROLLER_AXIS_LEFTY: SDL_GameControllerAxis = 1;
    pub const SDL_CONTROLLER_AXIS_RIGHTX: SDL_GameControllerAxis = 2;
    pub const SDL_CONTROLLER_AXIS_RIGHTY: SDL_GameControllerAxis = 3;
    pub const SDL_CONTROLLER_AXIS_TRIGGERLEFT: SDL_GameControllerAxis = 4;
    pub const SDL_CONTROLLER_AXIS_TRIGGERRIGHT: SDL_GameControllerAxis = 5;
    pub const SDL_CONTROLLER_AXIS_MAX: SDL_GameControllerAxis = 6;

    pub type SDL_GameControllerButton = c_int;
    pub const SDL_CONTROLLER_BUTTON_INVALID: SDL_GameControllerButton = -1;
    pub const SDL_CONTROLLER_BUTTON_A: SDL_GameControllerButton = 0;
    pub const SDL_CONTROLLER_BUTTON_B: SDL_GameControllerButton = 1;
    pub const SDL_CONTROLLER_BUTTON_X: SDL_GameControllerButton = 2;
    pub const SDL_CONTROLLER_BUTTON_Y: SDL_GameControllerButton = 3;
    pub const SDL_CONTROLLER_BUTTON_BACK: SDL_GameControllerButton = 4;
    pub const SDL_CONTROLLER_BUTTON_GUIDE: SDL_GameControllerButton = 5;
    pub const SDL_CONTROLLER_BUTTON_START: SDL_GameControllerButton = 6;
    pub const SDL_CONTROLLER_BUTTON_LEFTSTICK: SDL_GameControllerButton = 7;
    pub const SDL_CONTROLLER_BUTTON_RIGHTSTICK: SDL_GameControllerButton = 8;
    pub const SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SDL_GameControllerButton = 9;
    pub const SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SDL_GameControllerButton = 10;
    pub const SDL_CONTROLLER_BUTTON_DPAD_UP: SDL_GameControllerButton = 11;
    pub const SDL_CONTROLLER_BUTTON_DPAD_DOWN: SDL_GameControllerButton = 12;
    pub const SDL_CONTROLLER_BUTTON_DPAD_LEFT: SDL_GameControllerButton = 13;
    pub const SDL_CONTROLLER_BUTTON_DPAD_RIGHT: SDL_GameControllerButton = 14;
    pub const SDL_CONTROLLER_BUTTON_MAX: SDL_GameControllerButton = 15;

    extern "C" {
        pub fn SDL_GameControllerAddMapping(mappingString: *const c_char) -> c_int;
        pub fn SDL_GameControllerMappingForGUID(guid: SDL_JoystickGUID) ->
                  *const c_char;
        pub fn SDL_GameControllerMapping(gamecontroller: *const SDL_GameController)
                  -> *const c_char;
        pub fn SDL_IsGameController(joystick_index: c_int) -> SDL_bool;
        pub fn SDL_GameControllerNameForIndex(joystick_index: c_int) ->
                  *const c_char;
        pub fn SDL_GameControllerOpen(joystick_index: c_int) ->
                  *const SDL_GameController;
        pub fn SDL_GameControllerName(gamecontroller: *const SDL_GameController) ->
                  *const c_char;
        pub fn SDL_GameControllerGetAttached(gamecontroller:
                                                       *const SDL_GameController) ->
                  SDL_bool;
        pub fn SDL_GameControllerGetJoystick(gamecontroller:
                                                       *const SDL_GameController) ->
                  *const SDL_Joystick;
        pub fn SDL_GameControllerEventState(state: c_int) -> c_int;
        pub fn SDL_GameControllerUpdate();
        pub fn SDL_GameControllerGetAxisFromString(pchString: *const c_char) ->
                  SDL_GameControllerAxis;
        pub fn SDL_GameControllerGetStringForAxis(axis:
                                                            SDL_GameControllerAxis)
                  -> *const c_char;
        pub fn SDL_GameControllerGetBindForAxis(gamecontroller:
                                                          *const SDL_GameController,
                                                      axis: SDL_GameControllerAxis)
                  -> SDL_GameControllerButtonBind;
        pub fn SDL_GameControllerGetAxis(gamecontroller: *const SDL_GameController,
                                               axis: SDL_GameControllerAxis) ->
                  int16_t;
        pub fn SDL_GameControllerGetButtonFromString(pchString: *const c_char) ->
                  SDL_GameControllerButton;
        pub fn SDL_GameControllerGetStringForButton(button:
                                                          SDL_GameControllerButton)
                  -> *const c_char;
        pub fn SDL_GameControllerGetBindForButton(gamecontroller:
                                                            *const SDL_GameController,
                                                        button:
                                                          SDL_GameControllerButton)
                  -> SDL_GameControllerButtonBind;
        pub fn SDL_GameControllerGetButton(gamecontroller:
                                                     *const SDL_GameController,
                                                 button: SDL_GameControllerButton)
                  -> uint8_t;
        pub fn SDL_GameControllerClose(gamecontroller: *const SDL_GameController);
    }
}

#[deriving(PartialEq)]
#[repr(i32)]
pub enum ControllerAxis {
    InvalidAxis      = ll::SDL_CONTROLLER_AXIS_INVALID,
    LeftXAxis        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftYAxis        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightXAxis       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightYAxis       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeftAxis  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRightAxis = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

pub fn wrap_controller_axis(bitflags: u8) -> ControllerAxis {
    match bitflags as c_int {
        ll::SDL_CONTROLLER_AXIS_LEFTX        => ControllerAxis::LeftXAxis,
        ll::SDL_CONTROLLER_AXIS_LEFTY        => ControllerAxis::LeftYAxis,
        ll::SDL_CONTROLLER_AXIS_RIGHTX       => ControllerAxis::RightXAxis,
        ll::SDL_CONTROLLER_AXIS_RIGHTY       => ControllerAxis::RightYAxis,
        ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => ControllerAxis::TriggerLeftAxis,
        ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => ControllerAxis::TriggerRightAxis,
        _ => panic!("unhandled controller axis")
    }
}

#[deriving(PartialEq)]
#[repr(i32)]
pub enum ControllerButton {
    InvalidButton       = ll::SDL_CONTROLLER_BUTTON_INVALID,
    AButton             = ll::SDL_CONTROLLER_BUTTON_A,
    BButton             = ll::SDL_CONTROLLER_BUTTON_B,
    XButton             = ll::SDL_CONTROLLER_BUTTON_X,
    YButton             = ll::SDL_CONTROLLER_BUTTON_Y,
    BackButton          = ll::SDL_CONTROLLER_BUTTON_BACK,
    GuideButton         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    StartButton         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStickButton     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStickButton    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulderButton  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulderButton = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUpButton        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDownButton      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeftButton      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRightButton     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

pub fn wrap_controller_button(bitflags: u8) -> ControllerButton {
    match bitflags as c_int {
        ll::SDL_CONTROLLER_BUTTON_A             => ControllerButton::AButton,
        ll::SDL_CONTROLLER_BUTTON_B             => ControllerButton::BButton,
        ll::SDL_CONTROLLER_BUTTON_X             => ControllerButton::XButton,
        ll::SDL_CONTROLLER_BUTTON_Y             => ControllerButton::YButton,
        ll::SDL_CONTROLLER_BUTTON_BACK          => ControllerButton::BackButton,
        ll::SDL_CONTROLLER_BUTTON_GUIDE         => ControllerButton::GuideButton,
        ll::SDL_CONTROLLER_BUTTON_START         => ControllerButton::StartButton,
        ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => ControllerButton::LeftStickButton,
        ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => ControllerButton::RightStickButton,
        ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => ControllerButton::LeftShoulderButton,
        ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => ControllerButton::RightShoulderButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => ControllerButton::DPadUpButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => ControllerButton::DPadDownButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => ControllerButton::DPadLeftButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => ControllerButton::DPadRightButton,
        _ => panic!("unhandled controller button")
    }
}
<commit_msg>Remove "Axis" suffix from ControllerAxis enum variants.<commit_after>use libc::c_int;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_char, c_uchar, c_uint, c_void, int16_t, uint8_t};
    use joystick::ll::{SDL_Joystick, SDL_JoystickGUID};

    pub type SDL_bool = c_int;

    pub type SDL_GameController = c_void;

    pub type SDL_GameControllerBindType = c_uint;
    pub const SDL_CONTROLLER_BINDTYPE_NONE: SDL_GameControllerBindType = 0;
    pub const SDL_CONTROLLER_BINDTYPE_BUTTON: SDL_GameControllerBindType = 1;
    pub const SDL_CONTROLLER_BINDTYPE_AXIS: SDL_GameControllerBindType = 2;
    pub const SDL_CONTROLLER_BINDTYPE_HAT: SDL_GameControllerBindType = 3;

    #[allow(dead_code, non_snake_case)]
    #[repr(C)]
    pub struct SDL_GameControllerButtonBind {
        bindType: SDL_GameControllerBindType,
        value: SDL_GameControllerButtonBindData,
    }

    #[allow(dead_code)]
    #[repr(C)]
    pub struct SDL_GameControllerButtonBindData {
        data: [c_uchar, ..8u],
    }

    #[allow(dead_code)]
    pub struct SDL_GameControllerButtonBindDataHat {
        hat: c_int,
        hat_mask: c_int,
    }

    impl SDL_GameControllerButtonBindData {
        pub fn button(&self) -> *const c_int {
            self.data.as_ptr() as *const _
        }
        pub fn axis(&self) -> *const c_int {
            self.data.as_ptr() as *const _
        }
        pub fn hat(&self) -> *const SDL_GameControllerButtonBindDataHat {
            self.data.as_ptr() as *const _
        }
    }

    pub type SDL_GameControllerAxis = c_int;
    pub const SDL_CONTROLLER_AXIS_INVALID: SDL_GameControllerAxis = -1;
    pub const SDL_CONTROLLER_AXIS_LEFTX: SDL_GameControllerAxis = 0;
    pub const SDL_CONTROLLER_AXIS_LEFTY: SDL_GameControllerAxis = 1;
    pub const SDL_CONTROLLER_AXIS_RIGHTX: SDL_GameControllerAxis = 2;
    pub const SDL_CONTROLLER_AXIS_RIGHTY: SDL_GameControllerAxis = 3;
    pub const SDL_CONTROLLER_AXIS_TRIGGERLEFT: SDL_GameControllerAxis = 4;
    pub const SDL_CONTROLLER_AXIS_TRIGGERRIGHT: SDL_GameControllerAxis = 5;
    pub const SDL_CONTROLLER_AXIS_MAX: SDL_GameControllerAxis = 6;

    pub type SDL_GameControllerButton = c_int;
    pub const SDL_CONTROLLER_BUTTON_INVALID: SDL_GameControllerButton = -1;
    pub const SDL_CONTROLLER_BUTTON_A: SDL_GameControllerButton = 0;
    pub const SDL_CONTROLLER_BUTTON_B: SDL_GameControllerButton = 1;
    pub const SDL_CONTROLLER_BUTTON_X: SDL_GameControllerButton = 2;
    pub const SDL_CONTROLLER_BUTTON_Y: SDL_GameControllerButton = 3;
    pub const SDL_CONTROLLER_BUTTON_BACK: SDL_GameControllerButton = 4;
    pub const SDL_CONTROLLER_BUTTON_GUIDE: SDL_GameControllerButton = 5;
    pub const SDL_CONTROLLER_BUTTON_START: SDL_GameControllerButton = 6;
    pub const SDL_CONTROLLER_BUTTON_LEFTSTICK: SDL_GameControllerButton = 7;
    pub const SDL_CONTROLLER_BUTTON_RIGHTSTICK: SDL_GameControllerButton = 8;
    pub const SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SDL_GameControllerButton = 9;
    pub const SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SDL_GameControllerButton = 10;
    pub const SDL_CONTROLLER_BUTTON_DPAD_UP: SDL_GameControllerButton = 11;
    pub const SDL_CONTROLLER_BUTTON_DPAD_DOWN: SDL_GameControllerButton = 12;
    pub const SDL_CONTROLLER_BUTTON_DPAD_LEFT: SDL_GameControllerButton = 13;
    pub const SDL_CONTROLLER_BUTTON_DPAD_RIGHT: SDL_GameControllerButton = 14;
    pub const SDL_CONTROLLER_BUTTON_MAX: SDL_GameControllerButton = 15;

    extern "C" {
        pub fn SDL_GameControllerAddMapping(mappingString: *const c_char) -> c_int;
        pub fn SDL_GameControllerMappingForGUID(guid: SDL_JoystickGUID) ->
                  *const c_char;
        pub fn SDL_GameControllerMapping(gamecontroller: *const SDL_GameController)
                  -> *const c_char;
        pub fn SDL_IsGameController(joystick_index: c_int) -> SDL_bool;
        pub fn SDL_GameControllerNameForIndex(joystick_index: c_int) ->
                  *const c_char;
        pub fn SDL_GameControllerOpen(joystick_index: c_int) ->
                  *const SDL_GameController;
        pub fn SDL_GameControllerName(gamecontroller: *const SDL_GameController) ->
                  *const c_char;
        pub fn SDL_GameControllerGetAttached(gamecontroller:
                                                       *const SDL_GameController) ->
                  SDL_bool;
        pub fn SDL_GameControllerGetJoystick(gamecontroller:
                                                       *const SDL_GameController) ->
                  *const SDL_Joystick;
        pub fn SDL_GameControllerEventState(state: c_int) -> c_int;
        pub fn SDL_GameControllerUpdate();
        pub fn SDL_GameControllerGetAxisFromString(pchString: *const c_char) ->
                  SDL_GameControllerAxis;
        pub fn SDL_GameControllerGetStringForAxis(axis:
                                                            SDL_GameControllerAxis)
                  -> *const c_char;
        pub fn SDL_GameControllerGetBindForAxis(gamecontroller:
                                                          *const SDL_GameController,
                                                      axis: SDL_GameControllerAxis)
                  -> SDL_GameControllerButtonBind;
        pub fn SDL_GameControllerGetAxis(gamecontroller: *const SDL_GameController,
                                               axis: SDL_GameControllerAxis) ->
                  int16_t;
        pub fn SDL_GameControllerGetButtonFromString(pchString: *const c_char) ->
                  SDL_GameControllerButton;
        pub fn SDL_GameControllerGetStringForButton(button:
                                                          SDL_GameControllerButton)
                  -> *const c_char;
        pub fn SDL_GameControllerGetBindForButton(gamecontroller:
                                                            *const SDL_GameController,
                                                        button:
                                                          SDL_GameControllerButton)
                  -> SDL_GameControllerButtonBind;
        pub fn SDL_GameControllerGetButton(gamecontroller:
                                                     *const SDL_GameController,
                                                 button: SDL_GameControllerButton)
                  -> uint8_t;
        pub fn SDL_GameControllerClose(gamecontroller: *const SDL_GameController);
    }
}

#[deriving(PartialEq)]
#[repr(i32)]
pub enum ControllerAxis {
    Invalid      = ll::SDL_CONTROLLER_AXIS_INVALID,
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

pub fn wrap_controller_axis(bitflags: u8) -> ControllerAxis {
    match bitflags as c_int {
        ll::SDL_CONTROLLER_AXIS_LEFTX        => ControllerAxis::LeftX,
        ll::SDL_CONTROLLER_AXIS_LEFTY        => ControllerAxis::LeftY,
        ll::SDL_CONTROLLER_AXIS_RIGHTX       => ControllerAxis::RightX,
        ll::SDL_CONTROLLER_AXIS_RIGHTY       => ControllerAxis::RightY,
        ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => ControllerAxis::TriggerLeft,
        ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => ControllerAxis::TriggerRight,
        _ => panic!("unhandled controller axis")
    }
}

#[deriving(PartialEq)]
#[repr(i32)]
pub enum ControllerButton {
    InvalidButton       = ll::SDL_CONTROLLER_BUTTON_INVALID,
    AButton             = ll::SDL_CONTROLLER_BUTTON_A,
    BButton             = ll::SDL_CONTROLLER_BUTTON_B,
    XButton             = ll::SDL_CONTROLLER_BUTTON_X,
    YButton             = ll::SDL_CONTROLLER_BUTTON_Y,
    BackButton          = ll::SDL_CONTROLLER_BUTTON_BACK,
    GuideButton         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    StartButton         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStickButton     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStickButton    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulderButton  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulderButton = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUpButton        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDownButton      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeftButton      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRightButton     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

pub fn wrap_controller_button(bitflags: u8) -> ControllerButton {
    match bitflags as c_int {
        ll::SDL_CONTROLLER_BUTTON_A             => ControllerButton::AButton,
        ll::SDL_CONTROLLER_BUTTON_B             => ControllerButton::BButton,
        ll::SDL_CONTROLLER_BUTTON_X             => ControllerButton::XButton,
        ll::SDL_CONTROLLER_BUTTON_Y             => ControllerButton::YButton,
        ll::SDL_CONTROLLER_BUTTON_BACK          => ControllerButton::BackButton,
        ll::SDL_CONTROLLER_BUTTON_GUIDE         => ControllerButton::GuideButton,
        ll::SDL_CONTROLLER_BUTTON_START         => ControllerButton::StartButton,
        ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => ControllerButton::LeftStickButton,
        ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => ControllerButton::RightStickButton,
        ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => ControllerButton::LeftShoulderButton,
        ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => ControllerButton::RightShoulderButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => ControllerButton::DPadUpButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => ControllerButton::DPadDownButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => ControllerButton::DPadLeftButton,
        ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => ControllerButton::DPadRightButton,
        _ => panic!("unhandled controller button")
    }
}
<|endoftext|>