<commit_before>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::Editor;

use crate::core::view::LayoutEnv;

use crate::core::codec::text::u32_to_char;
use crate::core::codec::text::utf8;
use crate::core::codec::text::TextCodec;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

//

static COLOR_RED: (u8, u8, u8) = (195, 75, 0);
// static COLOR_GREEN: (u8, u8, u8) = (0, 128, 0);
static COLOR_GREEN: (u8, u8, u8) = (85, 170, 127);

static COLOR_ORANGE: (u8, u8, u8) = (247, 104, 38);

static COLOR_CYAN: (u8, u8, u8) = (86, 182, 185);

#[derive(Debug, PartialEq)]
enum TokenType {
    Unknown,
    InvalidUnicode,
    Blank, // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
    // Num,
    Identifier,   // _a-zA-Z unicode // default ?
    ParenOpen,    // (
    ParenClose,   // )
    BraceOpen,    // {
    BraceClose,   // }
    BracketOpen,  // [
    BracketClose, // ]
    SingleQuote,  // '
    DoubleQuote,  // "
    Comma,        // ,
    Semicolon,    // ;
    Ampersand,
    Equal,
    Plus,
    Minus,
    Mul,
    Div,
    Mod,
    LowerThan,
    GreaterThan,
}

pub struct HighlightFilter {
    token_io: Vec<FilterIo>,
    token_type: TokenType,
    utf8_token: Vec<u8>,
    new_color: (u8, u8, u8),
    utf8_codec: Box<dyn TextCodec>, // internal token representation is utf8
    skip_filter: bool,
    max_token_size: usize,
}

impl HighlightFilter {
    pub fn new() -> Self {
        HighlightFilter {
            token_io: Vec::new(),
            token_type: TokenType::Unknown,
            utf8_token: Vec::new(),
            new_color: TextStyle::default_color(),
            utf8_codec: Box::new(utf8::Utf8Codec::new()),
            skip_filter: false,
            max_token_size: 1024,
        }
    }
}

// TODO(ceg): monitor env.quit
// to flush
impl ContentFilter<'_> for HighlightFilter {
    fn name(&self) -> &'static str {
        &"HighlightFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        _view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.token_io = Vec::new();
        self.token_type = TokenType::Unknown;
        self.utf8_token = Vec::new();
        self.new_color = TextStyle::default_color();
        // self.utf8_codec =  Box::new(utf8::Utf8Codec::new());

        self.skip_filter = false;

        let p_input = crate::core::event::pending_input_event_count();
        if p_input > 255 {
            //dbg_println!("*** SKIP HIGHLIGHT *** p_input {}", p_input);
            self.skip_filter = true;
        }
        if env.screen.is_off_screen {
            self.skip_filter = true;
        }

        self.max_token_size = env.screen.width() * env.screen.height(); //
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.skip_filter {
            // return NOP hand let the caller skip swap
            *filter_out = filter_in.clone();
            return;
        }

        // flush too big token
        if self.token_io.len() > self.max_token_size {
            for mut io in self.token_io.iter_mut() {
                io.style.color = self.new_color;
            }
            filter_out.append(&mut self.token_io);
        }

        for io in filter_in {
            match &*io {
                FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let c = u32_to_char(*real_cp);

                    // dbg_println!("-----------");
                    // dbg_println!("parsing char : '{}'", c);

                    let token_type = match c {
                        '�' => TokenType::InvalidUnicode,
                        ' ' | '\n' | '\t' => TokenType::Blank,
                        '(' => TokenType::ParenOpen,
                        ')' => TokenType::ParenClose,
                        '{' => TokenType::BraceOpen,
                        '}' => TokenType::BraceClose,
                        '[' => TokenType::BracketOpen,
                        ']' => TokenType::BracketClose,
                        '\'' => TokenType::SingleQuote,
                        '"' => TokenType::DoubleQuote,
                        '=' => TokenType::Equal,
                        '*' => TokenType::Mul,
                        '+' => TokenType::Plus,
                        '-' => TokenType::Minus,
                        '/' => TokenType::Div,
                        '<' => TokenType::LowerThan,
                        '>' => TokenType::GreaterThan,
                        ',' => TokenType::Comma,
                        ';' => TokenType::Semicolon,
                        '&' => TokenType::Ampersand,
                        '%' => TokenType::Mod,
                        // '0'...'9' => TokenType::NUM,
                        _ => TokenType::Identifier,
                    };

                    // need more or accumulae same class
                    if self.token_io.is_empty() {
                        dbg_println!("self.token_io.is_empty()");

                        self.token_io.push(io.clone());
                        self.token_type = token_type;

                        continue;
                    }

                    if token_type == self.token_type && token_type != TokenType::InvalidUnicode {
                        self.token_io.push(io.clone());
                        continue;
                    }

                    // flush token
                    // dbg_println!("FLUSH prev TOKEN");

                    // build token utf8 string
                    for tok in self.token_io.iter() {
                        match tok {
                            &FilterIo {
                                data: FilterData::TextInfo { real_cp, .. },
                                ..
                            } => {
                                let mut utf8_out: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
                                let nr_bytes = self.utf8_codec.encode(real_cp, &mut utf8_out);
                                for b in utf8_out.iter().take(nr_bytes) {
                                    self.utf8_token.push(*b);
                                }
                            }
                            _ => {
                                panic!();
                            }
                        }
                    }

                    // select color
                    let token_str = if let Ok(s) = String::from_utf8(self.utf8_token.clone()) {
                        s
                    } else {
                        "�".to_string()
                    };

                    // dbg_println!("TOKEN_STR = '{}'", token_str);

                    self.new_color = match token_str.as_ref() {
                        // some Rust keywords
                        "use" | "crate" | "pub" => COLOR_RED,

                        // some Rust keywords
                        "let" | "mut" | "fn" | "impl" | "trait" => (0, 128, 128),

                        "str" | "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32"
                        | "i64" | "i128" | "f32" | "f64" => (0, 128, 128),

                        // C preprocessor
                        "#include" | "#if" | "#ifdef" | "#ifndef" | "#endif" | "#else"
                        | "#define" | "#pragma" => COLOR_RED,

                        // C keywords
                        "if" | "auto" | "break" | "case" | "char" | "const" | "continue"
                        | "default" | "do" | "double" | "else" | "enum" | "extern" | "float"
                        | "for" | "goto" | "int" | "long" | "register" | "return" | "short"
                        | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef"
                        | "union" | "unsigned" | "void" | "volatile" | "while" | "inline" => {
                            (0, 128, 128)
                        }

                        // C operators
                        "(" | ")" | "." | "->" | "+" | "-" | "*" | "/" | "%" | "=" | "==" | "<"
                        | ">" | "<=" | ">=" | "!=" | "&&" | "||" | "~" | "^" => COLOR_GREEN,

                        // easy hack, TODO(ceg): transform this module into proper tokenizer
                        "((" | "))" => COLOR_GREEN,
                        "(((" | ")))" => COLOR_GREEN,
                        "((((" | "))))" => COLOR_GREEN,
                        "(((((" | ")))))" => COLOR_GREEN,

                        "/*" | "*/" => (255, 255, 255),
                        "//" => (255, 255, 255),

                        // C++ keywords
                        "bool" | "class" | "template" | "namespace" => (0, 128, 128),

                        "true" | "false" => (35, 168, 242),

                        "\"" | "\"\"" | "'" | "''" => COLOR_ORANGE,

                        "," | ";" => (0, 128, 0),

                        "&" => COLOR_CYAN,

                        _ => {
                            let mut non_alnum = 0;
                            let mut digit_count = 0;

                            for c in self.utf8_token.iter() {
                                //dbg_println!("*c = {} b'0' {}", *c as u32, b'0' as u32);
                                //dbg_println!("*c = {} b'9' {}", *c as u32, b'9' as u32);
                                if *c >= b'0' && *c <= b'9' {
                                    digit_count += 1;
                                    continue;
                                }

                                if *c >= b'a' && *c <= b'f' {
                                    continue;
                                }

                                if *c >= b'A' && *c <= b'F' {
                                    continue;
                                }

                                non_alnum += 1;
                                break;
                            }

                            if non_alnum == 0 && digit_count != 0 {
                                (111, 100, 80)
                            } else {
                                self.new_color
                            }
                        }
                    };

                    self.token_type = token_type;

                    // flush token: set color
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);

                    // prepare next token
                    self.token_io.push(io.clone());

                    // reset state
                    self.utf8_token.clear();
                    self.new_color = TextStyle::default_color();
                }

                FilterIo {
                    data: FilterData::EndOfStream | FilterData::CustomLimitReached,
                    ..
                } => {
                    // flush pending token: set color
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);

                    // forward tag
                    filter_out.push(io.clone());
                }

                _ => {
                    dbg_println!("unexpected {:?}", io);
                    panic!("");
                }
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        // default
        if !self.token_io.is_empty() {
            // The parsing is incomplete
            // panic!("");
        }
    }
}
<commit_msg>highlight keywords: colorise ":" "::" "!"<commit_after>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::Editor;

use crate::core::view::LayoutEnv;

use crate::core::codec::text::u32_to_char;
use crate::core::codec::text::utf8;
use crate::core::codec::text::TextCodec;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

//

static COLOR_RED: (u8, u8, u8) = (195, 75, 0);
// static COLOR_GREEN: (u8, u8, u8) = (0, 128, 0);
static COLOR_GREEN: (u8, u8, u8) = (85, 170, 127);

static COLOR_ORANGE: (u8, u8, u8) = (247, 104, 38);

static COLOR_CYAN: (u8, u8, u8) = (86, 182, 185);

#[derive(Debug, PartialEq)]
enum TokenType {
    Unknown,
    InvalidUnicode,
    Blank, // ' ' | '\n' | '\t' : TODO(ceg): specific END_OF_LINE ?
    // Num,
    Identifier,   // _a-zA-Z unicode // default ?
    ParenOpen,    // (
    ParenClose,   // )
    BraceOpen,    // {
    BraceClose,   // }
    BracketOpen,  // [
    BracketClose, // ]
    SingleQuote,  // '
    DoubleQuote,  // "
    Comma,        // ,
    Colon,        // :
    Semicolon,    // ;
    Ampersand,
    Equal,
    Plus,
    Minus,
    Mul,
    Div,
    Mod,
    LowerThan,
    GreaterThan,
}

pub struct HighlightFilter {
    token_io: Vec<FilterIo>,
    token_type: TokenType,
    utf8_token: Vec<u8>,
    new_color: (u8, u8, u8),
    utf8_codec: Box<dyn TextCodec>, // internal token representation is utf8
    skip_filter: bool,
    max_token_size: usize,
}

impl HighlightFilter {
    pub fn new() -> Self {
        HighlightFilter {
            token_io: Vec::new(),
            token_type: TokenType::Unknown,
            utf8_token: Vec::new(),
            new_color: TextStyle::default_color(),
            utf8_codec: Box::new(utf8::Utf8Codec::new()),
            skip_filter: false,
            max_token_size: 1024,
        }
    }
}

// TODO(ceg): monitor env.quit
// to flush
impl ContentFilter<'_> for HighlightFilter {
    fn name(&self) -> &'static str {
        &"HighlightFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        _view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        self.token_io = Vec::new();
        self.token_type = TokenType::Unknown;
        self.utf8_token = Vec::new();
        self.new_color = TextStyle::default_color();
        // self.utf8_codec =  Box::new(utf8::Utf8Codec::new());

        self.skip_filter = false;

        let p_input = crate::core::event::pending_input_event_count();
        if p_input > 255 {
            //dbg_println!("*** SKIP HIGHLIGHT *** p_input {}", p_input);
            self.skip_filter = true;
        }
        if env.screen.is_off_screen {
            self.skip_filter = true;
        }

        self.max_token_size = env.screen.width() * env.screen.height(); //
    }

    fn run(
        &mut self,
        _view: &View,
        _env: &mut LayoutEnv,
        filter_in: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        if self.skip_filter {
            // return NOP hand let the caller skip swap
            *filter_out = filter_in.clone();
            return;
        }

        // flush too big token
        if self.token_io.len() > self.max_token_size {
            for mut io in self.token_io.iter_mut() {
                io.style.color = self.new_color;
            }
            filter_out.append(&mut self.token_io);
        }

        for io in filter_in {
            match &*io {
                FilterIo {
                    data: FilterData::TextInfo { real_cp, .. },
                    ..
                } => {
                    let c = u32_to_char(*real_cp);

                    // dbg_println!("-----------");
                    // dbg_println!("parsing char : '{}'", c);

                    let token_type = match c {
                        '�' => TokenType::InvalidUnicode,
                        ' ' | '\n' | '\t' => TokenType::Blank,
                        '(' => TokenType::ParenOpen,
                        ')' => TokenType::ParenClose,
                        '{' => TokenType::BraceOpen,
                        '}' => TokenType::BraceClose,
                        '[' => TokenType::BracketOpen,
                        ']' => TokenType::BracketClose,
                        '\'' => TokenType::SingleQuote,
                        '"' => TokenType::DoubleQuote,
                        '=' => TokenType::Equal,
                        '*' => TokenType::Mul,
                        '+' => TokenType::Plus,
                        '-' => TokenType::Minus,
                        '/' => TokenType::Div,
                        '<' => TokenType::LowerThan,
                        '>' => TokenType::GreaterThan,
                        ',' => TokenType::Comma,
                        ':' => TokenType::Colon,
                        ';' => TokenType::Semicolon,
                        '&' => TokenType::Ampersand,
                        '%' => TokenType::Mod,
                        // '0'...'9' => TokenType::NUM,
                        _ => TokenType::Identifier,
                    };

                    // need more or accumulae same class
                    if self.token_io.is_empty() {
                        dbg_println!("self.token_io.is_empty()");

                        self.token_io.push(io.clone());
                        self.token_type = token_type;

                        continue;
                    }

                    if token_type == self.token_type && token_type != TokenType::InvalidUnicode {
                        self.token_io.push(io.clone());
                        continue;
                    }

                    // flush token
                    // dbg_println!("FLUSH prev TOKEN");

                    // build token utf8 string
                    for tok in self.token_io.iter() {
                        match tok {
                            &FilterIo {
                                data: FilterData::TextInfo { real_cp, .. },
                                ..
                            } => {
                                let mut utf8_out: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
                                let nr_bytes = self.utf8_codec.encode(real_cp, &mut utf8_out);
                                for b in utf8_out.iter().take(nr_bytes) {
                                    self.utf8_token.push(*b);
                                }
                            }
                            _ => {
                                panic!();
                            }
                        }
                    }

                    // select color
                    let token_str = if let Ok(s) = String::from_utf8(self.utf8_token.clone()) {
                        s
                    } else {
                        "�".to_string()
                    };

                    // dbg_println!("TOKEN_STR = '{}'", token_str);

                    self.new_color = match token_str.as_ref() {
                        // some Rust keywords
                        "use" | "crate" | "pub" => COLOR_RED,

                        // some Rust keywords
                        "let" | "mut" | "fn" | "impl" | "trait" => (0, 128, 128),

                        "str" | "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32"
                        | "i64" | "i128" | "f32" | "f64" => (0, 128, 128),

                        // C preprocessor
                        "#include" | "#if" | "#ifdef" | "#ifndef" | "#endif" | "#else"
                        | "#define" | "#pragma" => COLOR_RED,

                        // C keywords
                        "if" | "auto" | "break" | "case" | "char" | "const" | "continue"
                        | "default" | "do" | "double" | "else" | "enum" | "extern" | "float"
                        | "for" | "goto" | "int" | "long" | "register" | "return" | "short"
                        | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef"
                        | "union" | "unsigned" | "void" | "volatile" | "while" | "inline" => {
                            (0, 128, 128)
                        }

                        // C operators
                        "(" | ")" | "." | "->" | "+" | "-" | "*" | "/" | "%" | "=" | "==" | "<" | "!"
                        | ">" | "<=" | ">=" | "!=" | "&&" | "||" | "~" | "^" => COLOR_GREEN,

                        // easy hack, TODO(ceg): transform this module into proper tokenizer
                        "((" | "))" => COLOR_GREEN,
                        "(((" | ")))" => COLOR_GREEN,
                        "((((" | "))))" => COLOR_GREEN,
                        "(((((" | ")))))" => COLOR_GREEN,

                        "/*" | "*/" => (255, 255, 255),
                        "//" => (255, 255, 255),

                        // C++ keywords
                        "bool" | "class" | "template" | "namespace" => (0, 128, 128),

                        "true" | "false" => (35, 168, 242),

                        "\"" | "\"\"" | "'" | "''" => COLOR_ORANGE,

                        "," | ":" | "::" | ";" => COLOR_GREEN,

                        "&" => COLOR_CYAN,

                        _ => {
                            let mut non_alnum = 0;
                            let mut digit_count = 0;

                            for c in self.utf8_token.iter() {
                                //dbg_println!("*c = {} b'0' {}", *c as u32, b'0' as u32);
                                //dbg_println!("*c = {} b'9' {}", *c as u32, b'9' as u32);
                                if *c >= b'0' && *c <= b'9' {
                                    digit_count += 1;
                                    continue;
                                }

                                if *c >= b'a' && *c <= b'f' {
                                    continue;
                                }

                                if *c >= b'A' && *c <= b'F' {
                                    continue;
                                }

                                non_alnum += 1;
                                break;
                            }

                            if non_alnum == 0 && digit_count != 0 {
                                (111, 100, 80)
                            } else {
                                self.new_color
                            }
                        }
                    };

                    self.token_type = token_type;

                    // flush token: set color
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);

                    // prepare next token
                    self.token_io.push(io.clone());

                    // reset state
                    self.utf8_token.clear();
                    self.new_color = TextStyle::default_color();
                }

                FilterIo {
                    data: FilterData::EndOfStream | FilterData::CustomLimitReached,
                    ..
                } => {
                    // flush pending token: set color
                    for mut io in self.token_io.iter_mut() {
                        io.style.color = self.new_color;
                    }
                    filter_out.append(&mut self.token_io);

                    // forward tag
                    filter_out.push(io.clone());
                }

                _ => {
                    dbg_println!("unexpected {:?}", io);
                    panic!("");
                }
            }
        }
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {
        // default
        if !self.token_io.is_empty() {
            // The parsing is incomplete
            // panic!("");
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{AppSettings, Arg, ArgMatches};

use cargo::{self, CliResult, Config};

use super::list_commands;
use super::commands;
use command_prelude::*;

pub fn main(config: &mut Config) -> CliResult {
    let args = cli().get_matches_safe()?;
    let is_verbose = args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = cargo::version();
        println!("{}", version);
        if is_verbose {
            println!(
                "release: {}.{}.{}",
                version.major, version.minor, version.patch
            );
            if let Some(ref cfg) = version.cfg_info {
                if let Some(ref ci) = cfg.commit_info {
                    println!("commit-hash: {}", ci.commit_hash);
                    println!("commit-date: {}", ci.commit_date);
                }
            }
        }
        return Ok(());
    }

    if let Some(ref code) = args.value_of("explain") {
        let mut procss = config.rustc()?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if args.is_present("list") {
        println!("Installed Commands:");
        for command in list_commands(config) {
            let (command, path) = command;
            if is_verbose {
                match path {
                    Some(p) => println!("    {:<20} {}", command, p),
                    None => println!("    {:<20}", command),
                }
            } else {
                println!("    {}", command);
            }
        }
        return Ok(());
    }

    if args.subcommand_name().is_none() {}

    execute_subcommand(config, args)
}

fn execute_subcommand(config: &mut Config, args: ArgMatches) -> CliResult {
    config.configure(
        args.occurrences_of("verbose") as u32,
        if args.is_present("quiet") {
            Some(true)
        } else {
            None
        },
        &args.value_of("color").map(|s| s.to_string()),
        args.is_present("frozen"),
        args.is_present("locked"),
        &args.values_of_lossy("unstable-features")
            .unwrap_or_default(),
    )?;

    let (cmd, args) = match args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            cli().print_help()?;
            return Ok(());
        }
    };

    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, args);
    }

    if let Some(mut alias) = super::aliased_command(config, cmd)? {
        alias.extend(
            args.values_of("")
                .unwrap_or_default()
                .map(|s| s.to_string()),
        );
        let args = cli()
            .setting(AppSettings::NoBinaryName)
            .get_matches_from_safe(alias)?;
        return execute_subcommand(config, args);
    }
    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

fn cli() -> App {
    let app = App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .about("")
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build       Compile the current project
    check       Analyze the current project and report errors, but don't build object files
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    init        Create a new cargo project in an existing directory
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this project to the registry
    install     Install a Rust binary
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            ).short("v")
                .multiple(true)
                .global(true),
        )
        .arg(
            opt("quiet", "No output printed to stdout")
                .short("q")
                .global(true),
        )
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin());
    app
}
<commit_msg>docs(Z-flags): Add description for each -Z flag in cargo -Z help<commit_after>extern crate clap;

use clap::{AppSettings, Arg, ArgMatches};

use cargo::{self, CliResult, Config};

use super::list_commands;
use super::commands;
use command_prelude::*;

pub fn main(config: &mut Config) -> CliResult {
    let args = cli().get_matches_safe()?;

    if args.value_of("unstable-features") == Some("help") {
        println!(
            "
Available unstable (nightly-only) flags:

    -Z avoid-dev-deps    -- Avoid installing dev-dependencies if possible
    -Z minimal-versions  -- Install minimal dependency versions instead of maximum
    -Z no-index-update   -- Do not update the registry, avoids a network request for benchmarking
    -Z offline           -- Offline mode that does not perform network requests
    -Z print-im-a-teapot -- Example option for demonstration purposes
    -Z unstable-options  -- Allow the usage of unstable options such as --registry

Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
        );
        return Ok(());
    }

    let is_verbose = args.occurrences_of("verbose") > 0;
    if args.is_present("version") {
        let version = cargo::version();
        println!("{}", version);
        if is_verbose {
            println!(
                "release: {}.{}.{}",
                version.major, version.minor, version.patch
            );
            if let Some(ref cfg) = version.cfg_info {
                if let Some(ref ci) = cfg.commit_info {
                    println!("commit-hash: {}", ci.commit_hash);
                    println!("commit-date: {}", ci.commit_date);
                }
            }
        }
        return Ok(());
    }

    if let Some(ref code) = args.value_of("explain") {
        let mut procss = config.rustc()?.process();
        procss.arg("--explain").arg(code).exec()?;
        return Ok(());
    }

    if args.is_present("list") {
        println!("Installed Commands:");
        for command in list_commands(config) {
            let (command, path) = command;
            if is_verbose {
                match path {
                    Some(p) => println!("    {:<20} {}", command, p),
                    None => println!("    {:<20}", command),
                }
            } else {
                println!("    {}", command);
            }
        }
        return Ok(());
    }

    if args.subcommand_name().is_none() {}

    execute_subcommand(config, args)
}

fn execute_subcommand(config: &mut Config, args: ArgMatches) -> CliResult {
    config.configure(
        args.occurrences_of("verbose") as u32,
        if args.is_present("quiet") {
            Some(true)
        } else {
            None
        },
        &args.value_of("color").map(|s| s.to_string()),
        args.is_present("frozen"),
        args.is_present("locked"),
        &args.values_of_lossy("unstable-features")
            .unwrap_or_default(),
    )?;

    let (cmd, args) = match args.subcommand() {
        (cmd, Some(args)) => (cmd, args),
        _ => {
            cli().print_help()?;
            return Ok(());
        }
    };

    if let Some(exec) = commands::builtin_exec(cmd) {
        return exec(config, args);
    }

    if let Some(mut alias) = super::aliased_command(config, cmd)? {
        alias.extend(
            args.values_of("")
                .unwrap_or_default()
                .map(|s| s.to_string()),
        );
        let args = cli()
            .setting(AppSettings::NoBinaryName)
            .get_matches_from_safe(alias)?;
        return execute_subcommand(config, args);
    }
    let mut ext_args: Vec<&str> = vec![cmd];
    ext_args.extend(args.values_of("").unwrap_or_default());
    super::execute_external_subcommand(config, cmd, &ext_args)
}

fn cli() -> App {
    let app = App::new("cargo")
        .settings(&[
            AppSettings::UnifiedHelpMessage,
            AppSettings::DeriveDisplayOrder,
            AppSettings::VersionlessSubcommands,
            AppSettings::AllowExternalSubcommands,
        ])
        .about("")
        .template(
            "\
Rust's package manager

USAGE:
    {usage}

OPTIONS:
{unified}

Some common cargo commands are (see all commands with --list):
    build       Compile the current project
    check       Analyze the current project and report errors, but don't build object files
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    init        Create a new cargo project in an existing directory
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this project to the registry
    install     Install a Rust binary
    uninstall   Uninstall a Rust binary

See 'cargo help <command>' for more information on a specific command.\n",
        )
        .arg(opt("version", "Print version info and exit").short("V"))
        .arg(opt("list", "List installed commands"))
        .arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
        .arg(
            opt(
                "verbose",
                "Use verbose output (-vv very verbose/build.rs output)",
            ).short("v")
                .multiple(true)
                .global(true),
        )
        .arg(
            opt("quiet", "No output printed to stdout")
                .short("q")
                .global(true),
        )
        .arg(
            opt("color", "Coloring: auto, always, never")
                .value_name("WHEN")
                .global(true),
        )
        .arg(opt("frozen", "Require Cargo.lock and cache are up to date").global(true))
        .arg(opt("locked", "Require Cargo.lock is up to date").global(true))
        .arg(
            Arg::with_name("unstable-features")
                .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
                .short("Z")
                .value_name("FLAG")
                .multiple(true)
                .number_of_values(1)
                .global(true),
        )
        .subcommands(commands::builtin());
    app
}
<|endoftext|>


<commit_before>// SID
extern crate rand;
use c64::memory;
use c64::cpu;
use c64::sid_tables::*;
use std::cell::RefCell;
use std::rc::Rc;

pub type SIDShared = Rc<RefCell<SID>>;

const SAMPLE_FREQ: u32 = 44100;  // output frequency
const SID_FREQ:    u32 = 985248; // SID frequency in Hz
const CALC_FREQ:   u32 = 50;     // frequency of calculating new buffer data (50Hz)
pub const SID_CYCLES:  u32 = SID_FREQ/SAMPLE_FREQ;  // SID clocks/sample frame
const NUM_SAMPLES: usize = 624; // size of buffer for sampled voice

enum WaveForm
{
    None,
    Triangle,
    Saw,
    TriSaw,
    Rectangle,
    TriRect,
    SawRect,
    TriSawRect,
    Noise
}

enum VoiceState
{
    Idle,
    Attack,
    Decay,
    Release
}

enum FilterType
{
    None,
    Lowpass,
    Bandpass,
    LowBandpass,
    Highpass,
    Notch,
    HighBandpass,
    All
}

struct SIDVoice
{
    wave: WaveForm,
    state: VoiceState,
    modulator: u8,   // number of voice that modulates this voice
    modulatee: u8,   // number of voice that this voice modulates
    wf_cnt: u32,     // waveform counter
    wf_add: u32,     // value to add to wf_cnt each frame
    freq: u16,
    pw_val: u16,     // pulse-width value
    attack_add: u32,
    decay_sub: u32,
    release_sub: u32,
    sustain_level: u32,
    level: u32,
    noise: u32,
    gate: bool,
    ring: bool,
    test: bool,
    filter: bool,
    sync: bool,
    mute: bool     // only voice 3 can be muted
}

impl SIDVoice
{
    fn new() -> SIDVoice
    {
        SIDVoice
        {
            wave: WaveForm::None,
            state: VoiceState::Idle,
            modulator: 0,
            modulatee: 0,
            wf_cnt: 0,
            wf_add: 0,
            freq: 0,
            pw_val: 0,
            attack_add: EG_TABLE[0],
            decay_sub: EG_TABLE[0],
            release_sub: EG_TABLE[0],
            sustain_level: 0,
            level: 0,
            noise: 0,
            gate: false,
            ring: false,
            test: false,
            filter: false,
            sync: false,
            mute: false
        }
    }
}

impl SIDVoice
{
    fn reset(&mut self)
    {
        self.wave = WaveForm::None;
        self.state = VoiceState::Idle;
        self.wf_cnt = 0;
        self.wf_add = 0;
        self.freq = 0;
        self.pw_val = 0;
        self.attack_add = EG_TABLE[0];
        self.decay_sub = EG_TABLE[0];
        self.release_sub = EG_TABLE[0];
        self.sustain_level = 0;
        self.level = 0;
        self.noise = 0;
        self.gate = false;
        self.ring = false;
        self.test = false;
        self.filter = false;
        self.sync = false;
        self.mute = false;
    }
}

pub struct SID
{
    mem_ref: Option<memory::MemShared>,
    last_sid_byte: u8,
    voices: Vec<SIDVoice>,
    rng: u32,
}

impl SID
{
    pub fn new_shared() -> SIDShared
    {
        let sid_shared = Rc::new(RefCell::new(SID
        {
            mem_ref: None,
            last_sid_byte: 0,
            voices: vec![SIDVoice::new(), SIDVoice::new(), SIDVoice::new()],
            rng: 1
        }));

        // calculate triangle table values
        unsafe
        {
            for i in 0..8192
            {
                let val = ((i << 4) | (i >> 8)) as u16;
                TRI_TABLE[i] = val;
                TRI_TABLE[8191 - i] = val;
            }
        }

        sid_shared.borrow_mut().voices[0].modulator = 2;
        sid_shared.borrow_mut().voices[0].modulatee = 1;
        sid_shared.borrow_mut().voices[1].modulator = 0;
        sid_shared.borrow_mut().voices[1].modulatee = 2;
        sid_shared.borrow_mut().voices[2].modulator = 1;
        sid_shared.borrow_mut().voices[2].modulatee = 0;

        sid_shared
    }

    pub fn set_references(&mut self, memref: memory::MemShared)
    {
        self.mem_ref = Some(memref);
    }

    pub fn reset(&mut self)
    {
        // TODO
        self.last_sid_byte = 0;

        for i in 0..self.voices.len()
        {
            self.voices[i].reset();
        }
    }

    fn lowpass_resonance(&self, f: f64) -> f64
    {
        let f2 = f * f;
        let f3 = f2 * f;
        let f4 = f3 * f;
        227.755 - f - 1.7653 * f - 0.0176385 * f2 + 0.00333484 * f3 - 9.05683E-6 * f4
    }

    fn highpass_resonance(&self, f: f64) -> f64
    {
        let f2 = f * f;
        let f3 = f2 * f;
        366.374 - 14.0052 * f + 0.603212 * f2 - 0.000880196 * f3
    }

    fn get_rand(&mut self) -> u8
    {
        self.rng = self.rng * 1103515245 + 12345;
        (self.rng >> 16) as u8
    }

    pub fn read_register(&mut self, addr: u16) -> u8
    {
        // most SID registers are write-only. The write to IO RAM is performed
        // so that the debugger can print out the value fetched by the CPU
        match addr
        {
            0xD419...0xD41A => {
                self.last_sid_byte = 0;
                let rval = 0xFF;
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            },
            0xD41B...0xD41C => {
                self.last_sid_byte = 0;
                let rval = rand::random::<u8>();
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            },
            0xD420...0xD7FF => self.read_register(0xD400 + (addr % 0x0020)),
            _               =>  {
                let rval = self.last_sid_byte;
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            }
        }
    }

    pub fn write_register(&mut self, addr: u16, value: u8, on_sid_write: &mut cpu::Callback)
    {
        self.last_sid_byte = value;
        match addr
        {
            // TODO
            0xD420...0xD7FF => self.write_register(0xD400 + (addr % 0x0020), value, on_sid_write),
            _               => as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, value)
        }

        *on_sid_write = cpu::Callback::None;
    }

    pub fn update(&mut self)
    {
        // TODO
    }
}
<commit_msg>initial filter calculations<commit_after>// SID
extern crate rand;
use c64::memory;
use c64::cpu;
use c64::sid_tables::*;
use std::cell::RefCell;
use std::rc::Rc;
use std::f32;

pub type SIDShared = Rc<RefCell<SID>>;

const SAMPLE_FREQ: u32 = 44100;  // output frequency
const SID_FREQ:    u32 = 985248; // SID frequency in Hz
const CALC_FREQ:   u32 = 50;     // frequency of calculating new buffer data (50Hz)
pub const SID_CYCLES:  u32 = SID_FREQ/SAMPLE_FREQ;  // SID clocks/sample frame
const NUM_SAMPLES: usize = 624; // size of buffer for sampled voice

enum WaveForm
{
    None,
    Triangle,
    Saw,
    TriSaw,
    Rectangle,
    TriRect,
    SawRect,
    TriSawRect,
    Noise
}

enum VoiceState
{
    Idle,
    Attack,
    Decay,
    Release
}

enum FilterType
{
    None,
    Lowpass,
    Bandpass,
    LowBandpass,
    Highpass,
    Notch,
    HighBandpass,
    All
}

struct SIDVoice
{
    wave: WaveForm,
    state: VoiceState,
    modulator: u8,   // number of voice that modulates this voice
    modulatee: u8,   // number of voice that this voice modulates
    wf_cnt: u32,     // waveform counter
    wf_add: u32,     // value to add to wf_cnt each frame
    freq: u16,
    pw_val: u16,     // pulse-width value
    attack_add: u32,
    decay_sub: u32,
    release_sub: u32,
    sustain_level: u32,
    level: u32,
    noise: u32,
    gate: bool,
    ring: bool,
    test: bool,
    filter: bool,
    sync: bool,
    mute: bool     // only voice 3 can be muted
}

impl SIDVoice
{
    fn new() -> SIDVoice
    {
        SIDVoice
        {
            wave: WaveForm::None,
            state: VoiceState::Idle,
            modulator: 0,
            modulatee: 0,
            wf_cnt: 0,
            wf_add: 0,
            freq: 0,
            pw_val: 0,
            attack_add: EG_TABLE[0],
            decay_sub: EG_TABLE[0],
            release_sub: EG_TABLE[0],
            sustain_level: 0,
            level: 0,
            noise: 0,
            gate: false,
            ring: false,
            test: false,
            filter: false,
            sync: false,
            mute: false
        }
    }
}

impl SIDVoice
{
    fn reset(&mut self)
    {
        self.wave = WaveForm::None;
        self.state = VoiceState::Idle;
        self.wf_cnt = 0;
        self.wf_add = 0;
        self.freq = 0;
        self.pw_val = 0;
        self.attack_add = EG_TABLE[0];
        self.decay_sub = EG_TABLE[0];
        self.release_sub = EG_TABLE[0];
        self.sustain_level = 0;
        self.level = 0;
        self.noise = 0;
        self.gate = false;
        self.ring = false;
        self.test = false;
        self.filter = false;
        self.sync = false;
        self.mute = false;
    }
}

pub struct SID
{
    mem_ref: Option<memory::MemShared>,
    last_sid_byte: u8,
    volume: u8,
    filter_type: FilterType,
    filter_freq: u8,
    filter_resonance: u8,

    // IIR filter
    iir_att: f32,
    d1: f32,
    d2: f32,
    g1: f32,
    g2: f32,
    xn1: f32,
    xn2: f32,
    yn1: f32,
    yn2: f32,

    voices: Vec<SIDVoice>,
    sample_buffer: [u8; NUM_SAMPLES],
    sample_idx: usize,
    rng: u32,
}

impl SID
{
    pub fn new_shared() -> SIDShared
    {
        let sid_shared = Rc::new(RefCell::new(SID
        {
            mem_ref: None,
            last_sid_byte: 0,
            voices: vec![SIDVoice::new(), SIDVoice::new(), SIDVoice::new()],
            volume: 0,
            filter_type: FilterType::None,
            filter_freq: 0,
            filter_resonance: 0,
            iir_att: 1.0,
            d1: 0.0,
            d2: 0.0,
            g1: 0.0,
            g2: 0.0,
            xn1: 0.0,
            xn2: 0.0,
            yn1: 0.0,
            yn2: 0.0,
            sample_buffer: [0; NUM_SAMPLES],
            sample_idx: 0,
            rng: 1
        }));

        // calculate triangle table values
        unsafe
        {
            for i in 0..8192
            {
                let val = ((i << 4) | (i >> 8)) as u16;
                TRI_TABLE[i] = val;
                TRI_TABLE[8191 - i] = val;
            }
        }

        sid_shared.borrow_mut().voices[0].modulator = 2;
        sid_shared.borrow_mut().voices[0].modulatee = 1;
        sid_shared.borrow_mut().voices[1].modulator = 0;
        sid_shared.borrow_mut().voices[1].modulatee = 2;
        sid_shared.borrow_mut().voices[2].modulator = 1;
        sid_shared.borrow_mut().voices[2].modulatee = 0;

        sid_shared
    }

    pub fn set_references(&mut self, memref: memory::MemShared)
    {
        self.mem_ref = Some(memref);
    }

    pub fn reset(&mut self)
    {
        self.last_sid_byte = 0;

        for i in 0..self.voices.len()
        {
            self.voices[i].reset();
        }

        self.volume = 0;
        self.filter_type = FilterType::None;
        self.filter_freq = 0;
        self.filter_resonance = 0;
        self.xn1 = 0.0;
        self.xn2 = 0.0;
        self.yn1 = 0.0;
        self.yn2 = 0.0;
        self.sample_idx = 0;
        self.calculate_filter();

        for i in 0..NUM_SAMPLES
        {
            self.sample_buffer[i] = 0;
        }
    }

    fn lowpass_resonance(&self, f: f32) -> f32
    {
        let f2 = f * f;
        let f3 = f2 * f;
        let f4 = f3 * f;
        227.755 - f - 1.7653 * f - 0.0176385 * f2 + 0.00333484 * f3 - 9.05683E-6 * f4
    }

    fn highpass_resonance(&self, f: f32) -> f32
    {
        let f2 = f * f;
        let f3 = f2 * f;
        366.374 - 14.0052 * f + 0.603212 * f2 - 0.000880196 * f3
    }

    fn calculate_filter(&mut self)
    {
        let f = self.filter_freq as f32;
        let mut resonance: f32 = 0.0;
        let mut arg: f32 = 0.0;

        match self.filter_type
        {
            FilterType::None => {
                self.d1 = 0.0;
                self.d2 = 0.0;
                self.g1 = 0.0;
                self.g2 = 0.0;
                self.iir_att = 1.0;
                return;
            },
            FilterType::All => {
                self.d1 = 0.0;
                self.d2 = 0.0;
                self.g1 = 0.0;
                self.g2 = 0.0;
                self.iir_att = 0.0;
                return;
            }
            FilterType::Lowpass | FilterType::LowBandpass => {
               resonance = self.lowpass_resonance(f);
            },
            _ => {
                resonance = self.highpass_resonance(f);
            }
        }

        arg = resonance / ((SAMPLE_FREQ >> 1) as f32);
        if arg > 0.99 { arg = 0.99; }
        if arg < 0.01 { arg = 0.01; }

        self.g2 = 0.55 + 1.2 * arg * arg - 1.2 * arg + resonance * 0.0133333333;
        self.g1 = -2.0 * self.g2.sqrt() * (f32::consts::PI * arg).cos();

        match self.filter_type {
            FilterType::LowBandpass | FilterType::HighBandpass => self.g2 += 0.1,
            _ => ()
        }

        if self.g1.abs() >= (self.g2 + 1.0)
        {
            if self.g1 > 0.0 { self.g1 = self.g2 + 0.99;    }
            else             { self.g1 = -(self.g2 + 0.99); }
        }

        match self.filter_type {
            FilterType::LowBandpass | FilterType::Lowpass => {
                self.d1 = 0.0;
                self.d2 = 1.0;
                self.iir_att = 0.25 * (1.0 + self.g1 + self.g2);
            },
            FilterType::HighBandpass | FilterType::Highpass => {
                self.d1 = -2.0;
                self.d2 = 1.0;
                self.iir_att = 0.25 * (1.0 - self.g1 + self.g2);
            },
            FilterType::Bandpass => {
                self.d1 = 0.0;
                self.d2 = -1.0;
                self.iir_att = 0.25 * (1.0 + self.g1 + self.g2) * (1.0 + (f32::consts::PI * arg).cos()) / (f32::consts::PI * arg).sin();
            },
            FilterType::Notch => {
                self.d1 = -2.0 * (f32::consts::PI * arg).cos();
                self.d2 = 1.0;
                self.iir_att = 0.25 * (1.0 + self.g1 + self.g2) * (1.0 + (f32::consts::PI * arg).cos()) / (f32::consts::PI * arg).sin();
            },
            _ => ()
        }
    }

    fn get_rand(&mut self) -> u8
    {
        self.rng = self.rng * 1103515245 + 12345;
        (self.rng >> 16) as u8
    }

    pub fn read_register(&mut self, addr: u16) -> u8
    {
        // most SID registers are write-only. The write to IO RAM is performed
        // so that the debugger can print out the value fetched by the CPU
        match addr
        {
            0xD419...0xD41A => {
                self.last_sid_byte = 0;
                let rval = 0xFF;
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            },
            0xD41B...0xD41C => {
                self.last_sid_byte = 0;
                let rval = rand::random::<u8>();
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            },
            0xD420...0xD7FF => self.read_register(0xD400 + (addr % 0x0020)),
            _               =>  {
                let rval = self.last_sid_byte;
                as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, rval);
                rval
            }
        }
    }

    pub fn write_register(&mut self, addr: u16, value: u8, on_sid_write: &mut cpu::Callback)
    {
        self.last_sid_byte = value;
        match addr
        {
            // TODO
            0xD420...0xD7FF => self.write_register(0xD400 + (addr % 0x0020), value, on_sid_write),
            _               => as_ref!(self.mem_ref).get_ram_bank(memory::MemType::IO).write(addr, value)
        }

        *on_sid_write = cpu::Callback::None;
    }

    pub fn update(&mut self)
    {
        let idx = self.sample_idx;
        self.sample_buffer[idx] = self.volume;
        self.sample_idx = (self.sample_idx + 1) % NUM_SAMPLES;
    }
}
<|endoftext|>


<commit_before>//! Connection helper.
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;

use tungstenite::client::uri_mode;
use tungstenite::handshake::client::Response;
use tungstenite::protocol::WebSocketConfig;
use tungstenite::Error;

use super::{client_async_with_config, IntoClientRequest, Request, WebSocketStream};

#[cfg(feature = "tls")]
pub(crate) mod encryption {
    use native_tls::TlsConnector as NativeTlsConnector;
    use tokio_native_tls::{TlsConnector as TokioTlsConnector, TlsStream};

    use tokio::io::{AsyncRead, AsyncWrite};

    use tungstenite::stream::Mode;
    use tungstenite::Error;

    use crate::stream::Stream as StreamSwitcher;

    /// A stream that might be protected with TLS.
    pub type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>;

    pub type AutoStream<S> = MaybeTlsStream<S>;

    /// A TLS connector that can be used when establishing TLS connections.
    pub type TlsConnector = NativeTlsConnector;

    pub async fn wrap_stream<S>(
        socket: S,
        domain: String,
        mode: Mode,
        tls_connector: Option<TlsConnector>,
    ) -> Result<AutoStream<S>, Error>
    where
        S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    {
        match mode {
            Mode::Plain => Ok(StreamSwitcher::Plain(socket)),
            Mode::Tls => {
                let try_connector = tls_connector.map_or_else(|| TlsConnector::new(), |c| Ok(c));
                let connector = try_connector.map_err(Error::Tls)?;
                let stream = TokioTlsConnector::from(connector);
                let connected = stream.connect(&domain, socket).await;
                match connected {
                    Err(e) => Err(Error::Tls(e)),
                    Ok(s) => Ok(StreamSwitcher::Tls(s)),
                }
            }
        }
    }
}

#[cfg(feature = "tls")]
pub use self::encryption::MaybeTlsStream;

pub use self::encryption::TlsConnector;

#[cfg(not(feature = "tls"))]
pub(crate) mod encryption {
    use tokio::io::{AsyncRead, AsyncWrite};

    use tungstenite::stream::Mode;
    use tungstenite::Error;

    pub type AutoStream<S> = S;

    /// A TLS connector that can be used when establishing TLS connections.
    pub type TlsConnector = ();

    pub async fn wrap_stream<S>(
        socket: S,
        _domain: String,
        mode: Mode,
        _tls_connector: Option<TlsConnector>,
    ) -> Result<AutoStream<S>, Error>
    where
        S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    {
        match mode {
            Mode::Plain => Ok(socket),
            Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())),
        }
    }
}

use self::encryption::{wrap_stream, AutoStream};

/// Get a domain from an URL.
#[inline]
fn domain(request: &Request) -> Result<String, Error> {
    match request.uri().host() {
        Some(d) => Ok(d.to_string()),
        None => Err(Error::Url("no host name in the url".into())),
    }
}

/// Creates a WebSocket handshake from a request and a stream,
/// upgrading the stream to TLS if required.
pub async fn client_async_tls<R, S>(
    request: R,
    stream: S,
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
    S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    AutoStream<S>: Unpin,
{
    client_async_tls_with_config(request, stream, None, None).await
}

/// The same as `client_async_tls()` but the one can specify a websocket configuration, and a
/// TLS connector.
///
/// Please refer to `client_async_tls()` for more details.
pub async fn client_async_tls_with_config<R, S>(
    request: R,
    stream: S,
    config: Option<WebSocketConfig>,
    tls_connector: Option<TlsConnector>,
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
    S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    AutoStream<S>: Unpin,
{
    let request = request.into_client_request()?;

    let domain = domain(&request)?;

    // Make sure we check domain and mode first. URL must be valid.
    let mode = uri_mode(&request.uri())?;

    let stream = wrap_stream(stream, domain, mode, tls_connector).await?;
    client_async_with_config(request, stream, config).await
}

/// Connect to a given URL.
pub async fn connect_async<R>(
    request: R,
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
{
    connect_async_with_config(request, None).await
}

/// The same as `connect_async()` but the one can specify a websocket configuration.
/// Please refer to `connect_async()` for more details.
pub async fn connect_async_with_config<R>(
    request: R,
    config: Option<WebSocketConfig>,
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
{
    let request = request.into_client_request()?;

    let domain = domain(&request)?;
    let port = request
        .uri()
        .port_u16()
        .or_else(|| match request.uri().scheme_str() {
            Some("wss") => Some(443),
            Some("ws") => Some(80),
            _ => None,
        })
        .ok_or_else(|| Error::Url("Url scheme not supported".into()))?;

    let addr = format!("{}:{}", domain, port);
    let try_socket = TcpStream::connect(addr).await;
    let socket = try_socket.map_err(Error::Io)?;
    client_async_tls_with_config(request, socket, config, None).await
}
<commit_msg>Update wording in `connect.rs`<commit_after>//! Connection helper.
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream;

use tungstenite::client::uri_mode;
use tungstenite::handshake::client::Response;
use tungstenite::protocol::WebSocketConfig;
use tungstenite::Error;

use super::{client_async_with_config, IntoClientRequest, Request, WebSocketStream};

#[cfg(feature = "tls")]
pub(crate) mod encryption {
    use native_tls::TlsConnector as NativeTlsConnector;
    use tokio_native_tls::{TlsConnector as TokioTlsConnector, TlsStream};

    use tokio::io::{AsyncRead, AsyncWrite};

    use tungstenite::stream::Mode;
    use tungstenite::Error;

    use crate::stream::Stream as StreamSwitcher;

    /// A stream that might be protected with TLS.
    pub type MaybeTlsStream<S> = StreamSwitcher<S, TlsStream<S>>;

    pub type AutoStream<S> = MaybeTlsStream<S>;

    /// A TLS connector that can be used when establishing TLS connections.
    pub type TlsConnector = NativeTlsConnector;

    pub async fn wrap_stream<S>(
        socket: S,
        domain: String,
        mode: Mode,
        tls_connector: Option<TlsConnector>,
    ) -> Result<AutoStream<S>, Error>
    where
        S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    {
        match mode {
            Mode::Plain => Ok(StreamSwitcher::Plain(socket)),
            Mode::Tls => {
                let try_connector = tls_connector.map_or_else(|| TlsConnector::new(), |c| Ok(c));
                let connector = try_connector.map_err(Error::Tls)?;
                let stream = TokioTlsConnector::from(connector);
                let connected = stream.connect(&domain, socket).await;
                match connected {
                    Err(e) => Err(Error::Tls(e)),
                    Ok(s) => Ok(StreamSwitcher::Tls(s)),
                }
            }
        }
    }
}

#[cfg(feature = "tls")]
pub use self::encryption::MaybeTlsStream;

pub use self::encryption::TlsConnector;

#[cfg(not(feature = "tls"))]
pub(crate) mod encryption {
    use tokio::io::{AsyncRead, AsyncWrite};

    use tungstenite::stream::Mode;
    use tungstenite::Error;

    pub type AutoStream<S> = S;

    /// A TLS connector that can be used when establishing TLS connections.
    pub type TlsConnector = ();

    pub async fn wrap_stream<S>(
        socket: S,
        _domain: String,
        mode: Mode,
        _tls_connector: Option<TlsConnector>,
    ) -> Result<AutoStream<S>, Error>
    where
        S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    {
        match mode {
            Mode::Plain => Ok(socket),
            Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())),
        }
    }
}

use self::encryption::{wrap_stream, AutoStream};

/// Get a domain from an URL.
#[inline]
fn domain(request: &Request) -> Result<String, Error> {
    match request.uri().host() {
        Some(d) => Ok(d.to_string()),
        None => Err(Error::Url("no host name in the url".into())),
    }
}

/// Creates a WebSocket handshake from a request and a stream,
/// upgrading the stream to TLS if required.
pub async fn client_async_tls<R, S>(
    request: R,
    stream: S,
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
    S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    AutoStream<S>: Unpin,
{
    client_async_tls_with_config(request, stream, None, None).await
}

/// The same as `client_async_tls()` but the one can specify a websocket configuration,
/// and an optional TLS connector. If no connector is specified, the default one will
/// be created.
///
/// Please refer to `client_async_tls()` for more details.
pub async fn client_async_tls_with_config<R, S>(
    request: R,
    stream: S,
    config: Option<WebSocketConfig>,
    tls_connector: Option<TlsConnector>,
) -> Result<(WebSocketStream<AutoStream<S>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
    S: 'static + AsyncRead + AsyncWrite + Send + Unpin,
    AutoStream<S>: Unpin,
{
    let request = request.into_client_request()?;

    let domain = domain(&request)?;

    // Make sure we check domain and mode first. URL must be valid.
    let mode = uri_mode(&request.uri())?;

    let stream = wrap_stream(stream, domain, mode, tls_connector).await?;
    client_async_with_config(request, stream, config).await
}

/// Connect to a given URL.
pub async fn connect_async<R>(
    request: R,
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
{
    connect_async_with_config(request, None).await
}

/// The same as `connect_async()` but the one can specify a websocket configuration.
/// Please refer to `connect_async()` for more details.
pub async fn connect_async_with_config<R>(
    request: R,
    config: Option<WebSocketConfig>,
) -> Result<(WebSocketStream<AutoStream<TcpStream>>, Response), Error>
where
    R: IntoClientRequest + Unpin,
{
    let request = request.into_client_request()?;

    let domain = domain(&request)?;
    let port = request
        .uri()
        .port_u16()
        .or_else(|| match request.uri().scheme_str() {
            Some("wss") => Some(443),
            Some("ws") => Some(80),
            _ => None,
        })
        .ok_or_else(|| Error::Url("Url scheme not supported".into()))?;

    let addr = format!("{}:{}", domain, port);
    let try_socket = TcpStream::connect(addr).await;
    let socket = try_socket.map_err(Error::Io)?;
    client_async_tls_with_config(request, socket, config, None).await
}
<|endoftext|>


<commit_before>//! The `context` module contains functions and traits related to using the `Context` type.

use std::fmt;
/// We re-export winit so it's easy for people to use the same version as we are
/// without having to mess around figuring it out.
pub use winit;

#[cfg(feature = "audio")]
use crate::audio;
use crate::conf;
use crate::error::GameResult;
use crate::filesystem::Filesystem;
use crate::graphics;
use crate::graphics::GraphicsContext;
use crate::input;
use crate::timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type can **not**
/// be shared/sent between threads and only one `Context` can exist at a time.  Trying
/// to create a second one will fail.  It is fine to drop a `Context`
/// and create a new one, but this will also close and re-open your
/// game's window.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources will need
/// to access one, or rarely even two, of its sub-contexts.
/// It is an error to create some type that
/// relies upon a `Context`, such as `Image`, and then drop the `Context`
/// and try to draw the old `Image` with the new `Context`.
///
/// The fields in this struct used to be basically undocumented features,
/// only here to make it easier to debug, or to let advanced users
/// hook into the guts of ggez and make it do things it normally
/// can't. Now that `ggez`'s module-level functions, taking the whole `Context`
/// have been deprecated, calling their methods directly is recommended.
pub struct Context {
    /// Filesystem state.
    pub fs: Filesystem,
    /// Graphics state.
    pub gfx: GraphicsContext,
    /// Timer state.
    pub time: timer::TimeContext,
    /// Audio context.
    #[cfg(feature = "audio")]
    pub audio: audio::AudioContext,
    /// Keyboard input context.
    pub keyboard: input::keyboard::KeyboardContext,
    /// Mouse input context.
    pub mouse: input::mouse::MouseContext,
    /// Gamepad input context.
    #[cfg(feature = "gamepad")]
    pub gamepad: input::gamepad::GamepadContext,

    /// The Conf object the Context was created with.
    /// It's here just so that we can see the original settings,
    /// updating it will have no effect.
    pub(crate) conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    /// This is internally controlled by the outcome of [`quit_event`](crate::event::EventHandler::quit_event), requested through [`event::request_quit()`](crate::event::request_quit).
    pub continuing: bool,
    /// Whether or not a `quit_event` has been requested.
    /// Set this with [`ggez::event::request_quit()`](crate::event::request_quit).
    ///
    /// It's exposed here for people who want to roll their own event loop.
    pub quit_requested: bool,
}

// This is ugly and hacky but greatly improves ergonomics.

/// Used to represent types that can provide a certain context type.
///
/// If you don't know what this is, you most likely want to pass `ctx`.
///
/// This trait is basically syntactical sugar, saving you from having
/// to split contexts when you don't need to and also shortening calls like
/// ```rust
/// # use ggez::GameResult;
/// # fn t(ctx: &mut ggez::Context, canvas: ggez::graphics::Canvas) -> GameResult {
/// canvas.finish(&mut ctx.gfx)?;
/// # Ok(())
/// # }
/// ```
/// into just
/// ```rust
/// # use ggez::GameResult;
/// # fn t(ctx: &mut ggez::Context, canvas: ggez::graphics::Canvas) -> GameResult {
/// canvas.finish(ctx)?;
/// # Ok(())
/// # }
/// ```
pub trait Has<T> {
    /// Method to retrieve the context type.
    fn retrieve(&self) -> &T;
}

impl<T> Has<T> for T {
    #[inline]
    fn retrieve(&self) -> &T {
        self
    }
}

impl Has<Filesystem> for Context {
    #[inline]
    fn retrieve(&self) -> &Filesystem {
        &self.fs
    }
}

impl Has<GraphicsContext> for Context {
    #[inline]
    fn retrieve(&self) -> &GraphicsContext {
        &self.gfx
    }
}

#[cfg(feature = "audio")]
impl Has<audio::AudioContext> for Context {
    #[inline]
    fn retrieve(&self) -> &audio::AudioContext {
        &self.audio
    }
}

/// Used to represent types that can provide a certain context type in a mutable form.
/// See also [`Has<T>`].
///
/// If you don't know what this is, you most likely want to pass `ctx`.
pub trait HasMut<T> {
    /// Method to retrieve the context type as mutable.
    fn retrieve_mut(&mut self) -> &mut T;
}

impl<T> HasMut<T> for T {
    #[inline]
    fn retrieve_mut(&mut self) -> &mut T {
        self
    }
}

impl HasMut<GraphicsContext> for Context {
    #[inline]
    fn retrieve_mut(&mut self) -> &mut GraphicsContext {
        &mut self.gfx
    }
}

/// Used to represent types that can provide two context types. See also [`Has<T>`].
///
/// If you don't know what this is, you most likely want to pass `ctx`.
pub trait HasTwo<T, U> {
    /// Method to retrieve the first context type.
    fn retrieve_first(&self) -> &T;
    /// Method to retrieve the second context type.
    fn retrieve_second(&self) -> &U;
}

impl<T, U> HasTwo<T, U> for (&T, &U) {
    #[inline]
    fn retrieve_first(&self) -> &T {
        self.0
    }

    #[inline]
    fn retrieve_second(&self) -> &U {
        self.1
    }
}

impl HasTwo<Filesystem, GraphicsContext> for Context {
    #[inline]
    fn retrieve_first(&self) -> &Filesystem {
        &self.fs
    }

    #[inline]
    fn retrieve_second(&self) -> &GraphicsContext {
        &self.gfx
    }
}

#[cfg(feature = "audio")]
impl HasTwo<Filesystem, crate::audio::AudioContext> for Context {
    fn retrieve_first(&self) -> &Filesystem {
        &self.fs
    }

    fn retrieve_second(&self) -> &crate::audio::AudioContext {
        &self.audio
    }
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

impl Context {
    /// Tries to create a new Context using settings from the given [`Conf`](../conf/struct.Conf.html) object.
    /// Usually called by [`ContextBuilder::build()`](struct.ContextBuilder.html#method.build).
    fn from_conf(
        conf: conf::Conf,
        mut fs: Filesystem,
    ) -> GameResult<(Context, winit::event_loop::EventLoop<()>)> {
        #[cfg(feature = "audio")]
        let audio_context = audio::AudioContext::new()?;
        let events_loop = winit::event_loop::EventLoop::new();
        let timer_context = timer::TimeContext::new();
        let graphics_context =
            graphics::context::GraphicsContext::new(&events_loop, &conf, &mut fs)?;

        let ctx = Context {
            conf,
            fs,
            gfx: graphics_context,
            continuing: true,
            quit_requested: false,
            time: timer_context,
            #[cfg(feature = "audio")]
            audio: audio_context,
            keyboard: input::keyboard::KeyboardContext::new(),
            mouse: input::mouse::MouseContext::new(),
            #[cfg(feature = "gamepad")]
            gamepad: input::gamepad::GamepadContext::new()?,
        };

        Ok((ctx, events_loop))
    }
}

use std::borrow::Cow;
use std::path;

/// A builder object for creating a [`Context`](struct.Context.html).
#[derive(Debug, Clone, PartialEq)]
pub struct ContextBuilder {
    pub(crate) game_id: String,
    pub(crate) author: String,
    pub(crate) conf: conf::Conf,
    pub(crate) resources_dir_name: String,
    pub(crate) resources_zip_name: String,
    pub(crate) paths: Vec<path::PathBuf>,
    pub(crate) memory_zip_files: Vec<Cow<'static, [u8]>>,
    pub(crate) load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new `ContextBuilder` with default settings.
    pub fn new(game_id: &str, author: &str) -> Self {
        Self {
            game_id: game_id.to_string(),
            author: author.to_string(),
            conf: conf::Conf::default(),
            resources_dir_name: "resources".to_string(),
            resources_zip_name: "resources.zip".to_string(),
            paths: vec![],
            memory_zip_files: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings.
    #[must_use]
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings.
    #[must_use]
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend.
    #[must_use]
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Sets all the config options, overriding any previous
    /// ones from [`window_setup()`](#method.window_setup),
    /// [`window_mode()`](#method.window_mode), and
    /// [`backend()`](#method.backend).  These are used as
    /// defaults and are overridden by any external config
    /// file found.
    #[must_use]
    pub fn default_conf(mut self, conf: conf::Conf) -> Self {
        self.conf = conf;
        self
    }

    /// Sets resources dir name.
    /// Default resources dir name is `resources`.
    #[must_use]
    pub fn resources_dir_name(mut self, new_name: impl ToString) -> Self {
        self.resources_dir_name = new_name.to_string();
        self
    }

    /// Sets resources zip name.
    /// Default resources dir name is `resources.zip`.
    #[must_use]
    pub fn resources_zip_name(mut self, new_name: impl ToString) -> Self {
        self.resources_zip_name = new_name.to_string();
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    #[must_use]
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Add a new zip file from bytes whose contents will be searched
    /// for resources. The zip file will be stored in-memory.
    /// You can pass it a static slice, a `Vec` of bytes, etc.
    ///
    /// ```ignore
    /// use ggez::context::ContextBuilder;
    /// let _ = ContextBuilder::new()
    ///     .add_zipfile_bytes(include_bytes!("../resources.zip").to_vec())
    ///     .build();
    /// ```
    #[must_use]
    pub fn add_zipfile_bytes<B>(mut self, bytes: B) -> Self
    where
        B: Into<Cow<'static, [u8]>>,
    {
        let cow = bytes.into();
        self.memory_zip_files.push(cow);
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    #[must_use]
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the `Context`.
    pub fn build(self) -> GameResult<(Context, winit::event_loop::EventLoop<()>)> {
        let mut fs = Filesystem::new(
            self.game_id.as_ref(),
            self.author.as_ref(),
            &self.resources_dir_name,
            &self.resources_zip_name,
        )?;

        for path in &self.paths {
            fs.mount(path, true);
        }

        for zipfile_bytes in self.memory_zip_files {
            fs.add_zip_file(std::io::Cursor::new(zipfile_bytes))?;
        }

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        Context::from_conf(config, fs)
    }
}
<commit_msg>Improves the way HasTwo is implemented and add a test for them<commit_after>//! The `context` module contains functions and traits related to using the `Context` type.

use std::fmt;
/// We re-export winit so it's easy for people to use the same version as we are
/// without having to mess around figuring it out.
pub use winit;

#[cfg(feature = "audio")]
use crate::audio;
use crate::conf;
use crate::error::GameResult;
use crate::filesystem::Filesystem;
use crate::graphics;
use crate::graphics::GraphicsContext;
use crate::input;
use crate::timer;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type can **not**
/// be shared/sent between threads and only one `Context` can exist at a time.  Trying
/// to create a second one will fail.  It is fine to drop a `Context`
/// and create a new one, but this will also close and re-open your
/// game's window.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources will need
/// to access one, or rarely even two, of its sub-contexts.
/// It is an error to create some type that
/// relies upon a `Context`, such as `Image`, and then drop the `Context`
/// and try to draw the old `Image` with the new `Context`.
///
/// The fields in this struct used to be basically undocumented features,
/// only here to make it easier to debug, or to let advanced users
/// hook into the guts of ggez and make it do things it normally
/// can't. Now that `ggez`'s module-level functions, taking the whole `Context`
/// have been deprecated, calling their methods directly is recommended.
pub struct Context {
    /// Filesystem state.
    pub fs: Filesystem,
    /// Graphics state.
    pub gfx: GraphicsContext,
    /// Timer state.
    pub time: timer::TimeContext,
    /// Audio context.
    #[cfg(feature = "audio")]
    pub audio: audio::AudioContext,
    /// Keyboard input context.
    pub keyboard: input::keyboard::KeyboardContext,
    /// Mouse input context.
    pub mouse: input::mouse::MouseContext,
    /// Gamepad input context.
    #[cfg(feature = "gamepad")]
    pub gamepad: input::gamepad::GamepadContext,

    /// The Conf object the Context was created with.
    /// It's here just so that we can see the original settings,
    /// updating it will have no effect.
    pub(crate) conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    /// This is internally controlled by the outcome of [`quit_event`](crate::event::EventHandler::quit_event), requested through [`event::request_quit()`](crate::event::request_quit).
    pub continuing: bool,
    /// Whether or not a `quit_event` has been requested.
    /// Set this with [`ggez::event::request_quit()`](crate::event::request_quit).
    ///
    /// It's exposed here for people who want to roll their own event loop.
    pub quit_requested: bool,
}

// This is ugly and hacky but greatly improves ergonomics.

/// Used to represent types that can provide a certain context type.
///
/// If you don't know what this is, you most likely want to pass `ctx`.
///
/// This trait is basically syntactical sugar, saving you from having
/// to split contexts when you don't need to and also shortening calls like
/// ```rust
/// # use ggez::GameResult;
/// # fn t(ctx: &mut ggez::Context, canvas: ggez::graphics::Canvas) -> GameResult {
/// canvas.finish(&mut ctx.gfx)?;
/// # Ok(())
/// # }
/// ```
/// into just
/// ```rust
/// # use ggez::GameResult;
/// # fn t(ctx: &mut ggez::Context, canvas: ggez::graphics::Canvas) -> GameResult {
/// canvas.finish(ctx)?;
/// # Ok(())
/// # }
/// ```
pub trait Has<T> {
    /// Method to retrieve the context type.
    fn retrieve(&self) -> &T;
}

impl<T> Has<T> for T {
    #[inline]
    fn retrieve(&self) -> &T {
        self
    }
}

impl Has<Filesystem> for Context {
    #[inline]
    fn retrieve(&self) -> &Filesystem {
        &self.fs
    }
}

impl Has<GraphicsContext> for Context {
    #[inline]
    fn retrieve(&self) -> &GraphicsContext {
        &self.gfx
    }
}

#[cfg(feature = "audio")]
impl Has<audio::AudioContext> for Context {
    #[inline]
    fn retrieve(&self) -> &audio::AudioContext {
        &self.audio
    }
}

/// Used to represent types that can provide a certain context type in a mutable form.
/// See also [`Has<T>`].
///
/// If you don't know what this is, you most likely want to pass `ctx`.
pub trait HasMut<T> {
    /// Method to retrieve the context type as mutable.
    fn retrieve_mut(&mut self) -> &mut T;
}

impl<T> HasMut<T> for T {
    #[inline]
    fn retrieve_mut(&mut self) -> &mut T {
        self
    }
}

impl HasMut<GraphicsContext> for Context {
    #[inline]
    fn retrieve_mut(&mut self) -> &mut GraphicsContext {
        &mut self.gfx
    }
}

/// Used to represent types that can provide two context types. See also [`Has<T>`].
///
/// If you don't know what this is, you most likely want to pass `ctx`.
pub trait HasTwo<T, U> {
    /// Method to retrieve the first context type.
    fn retrieve_first(&self) -> &T;
    /// Method to retrieve the second context type.
    fn retrieve_second(&self) -> &U;
}

impl<T, U> HasTwo<T, U> for Context
where
    Context: Has<T> + Has<U>,
{
    #[inline]
    fn retrieve_first(&self) -> &T {
        Has::<T>::retrieve(self)
    }

    #[inline]
    fn retrieve_second(&self) -> &U {
        Has::<U>::retrieve(self)
    }
}

impl<T, U> HasTwo<T, U> for (&T, &U) {
    #[inline]
    fn retrieve_first(&self) -> &T {
        self.0
    }

    #[inline]
    fn retrieve_second(&self) -> &U {
        self.1
    }
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

impl Context {
    /// Tries to create a new Context using settings from the given [`Conf`](../conf/struct.Conf.html) object.
    /// Usually called by [`ContextBuilder::build()`](struct.ContextBuilder.html#method.build).
    fn from_conf(
        conf: conf::Conf,
        mut fs: Filesystem,
    ) -> GameResult<(Context, winit::event_loop::EventLoop<()>)> {
        #[cfg(feature = "audio")]
        let audio_context = audio::AudioContext::new()?;
        let events_loop = winit::event_loop::EventLoop::new();
        let timer_context = timer::TimeContext::new();
        let graphics_context =
            graphics::context::GraphicsContext::new(&events_loop, &conf, &mut fs)?;

        let ctx = Context {
            conf,
            fs,
            gfx: graphics_context,
            continuing: true,
            quit_requested: false,
            time: timer_context,
            #[cfg(feature = "audio")]
            audio: audio_context,
            keyboard: input::keyboard::KeyboardContext::new(),
            mouse: input::mouse::MouseContext::new(),
            #[cfg(feature = "gamepad")]
            gamepad: input::gamepad::GamepadContext::new()?,
        };

        Ok((ctx, events_loop))
    }
}

use std::borrow::Cow;
use std::path;

/// A builder object for creating a [`Context`](struct.Context.html).
#[derive(Debug, Clone, PartialEq)]
pub struct ContextBuilder {
    pub(crate) game_id: String,
    pub(crate) author: String,
    pub(crate) conf: conf::Conf,
    pub(crate) resources_dir_name: String,
    pub(crate) resources_zip_name: String,
    pub(crate) paths: Vec<path::PathBuf>,
    pub(crate) memory_zip_files: Vec<Cow<'static, [u8]>>,
    pub(crate) load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new `ContextBuilder` with default settings.
    pub fn new(game_id: &str, author: &str) -> Self {
        Self {
            game_id: game_id.to_string(),
            author: author.to_string(),
            conf: conf::Conf::default(),
            resources_dir_name: "resources".to_string(),
            resources_zip_name: "resources.zip".to_string(),
            paths: vec![],
            memory_zip_files: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings.
    #[must_use]
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings.
    #[must_use]
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend.
    #[must_use]
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Sets all the config options, overriding any previous
    /// ones from [`window_setup()`](#method.window_setup),
    /// [`window_mode()`](#method.window_mode), and
    /// [`backend()`](#method.backend).  These are used as
    /// defaults and are overridden by any external config
    /// file found.
    #[must_use]
    pub fn default_conf(mut self, conf: conf::Conf) -> Self {
        self.conf = conf;
        self
    }

    /// Sets resources dir name.
    /// Default resources dir name is `resources`.
    #[must_use]
    pub fn resources_dir_name(mut self, new_name: impl ToString) -> Self {
        self.resources_dir_name = new_name.to_string();
        self
    }

    /// Sets resources zip name.
    /// Default resources dir name is `resources.zip`.
    #[must_use]
    pub fn resources_zip_name(mut self, new_name: impl ToString) -> Self {
        self.resources_zip_name = new_name.to_string();
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    #[must_use]
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Add a new zip file from bytes whose contents will be searched
    /// for resources. The zip file will be stored in-memory.
    /// You can pass it a static slice, a `Vec` of bytes, etc.
    ///
    /// ```ignore
    /// use ggez::context::ContextBuilder;
    /// let _ = ContextBuilder::new()
    ///     .add_zipfile_bytes(include_bytes!("../resources.zip").to_vec())
    ///     .build();
    /// ```
    #[must_use]
    pub fn add_zipfile_bytes<B>(mut self, bytes: B) -> Self
    where
        B: Into<Cow<'static, [u8]>>,
    {
        let cow = bytes.into();
        self.memory_zip_files.push(cow);
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    #[must_use]
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the `Context`.
    pub fn build(self) -> GameResult<(Context, winit::event_loop::EventLoop<()>)> {
        let mut fs = Filesystem::new(
            self.game_id.as_ref(),
            self.author.as_ref(),
            &self.resources_dir_name,
            &self.resources_zip_name,
        )?;

        for path in &self.paths {
            fs.mount(path, true);
        }

        for zipfile_bytes in self.memory_zip_files {
            fs.add_zip_file(std::io::Cursor::new(zipfile_bytes))?;
        }

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        Context::from_conf(config, fs)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        context::{Has, HasMut, HasTwo},
        filesystem::Filesystem,
        graphics::GraphicsContext,
        ContextBuilder,
    };

    #[test]
    fn has_traits() {
        let (mut ctx, _event_loop) = ContextBuilder::new("test", "ggez").build().unwrap();

        fn takes_gfx(_gfx: &impl Has<GraphicsContext>) {}
        takes_gfx(&ctx);
        takes_gfx(&ctx.gfx);

        fn takes_mut_gfx(_gfx: &mut impl HasMut<GraphicsContext>) {}
        takes_mut_gfx(&mut ctx);
        takes_mut_gfx(&mut ctx.gfx);

        fn takes_gfx_fs(_gfx_fs: &impl HasTwo<GraphicsContext, Filesystem>) {}
        takes_gfx_fs(&ctx);
        takes_gfx_fs(&(&ctx.gfx, &ctx.fs));
    }
}
<|endoftext|>