<commit_before>enum MyEnum {
    MyEmptyVariant,
    MyNewtypeVariant(i32),
    MyTupleVariant(i32, i32),
    MyStructVariant {
        my_first_field: i32,
        my_second_field: i32,
    }
}

fn test(me: MyEnum) {
    match me {
        MyEnum::MyEmptyVariant => {},
        MyEnum::MyNewtypeVariant(ref val) => assert_eq!(val, &42),
        MyEnum::MyTupleVariant(ref a, ref b) => {
            assert_eq!(a, &43);
            assert_eq!(b, &44);
        },
        MyEnum::MyStructVariant { ref my_first_field, ref my_second_field } => {
            assert_eq!(my_first_field, &45);
            assert_eq!(my_second_field, &46);
        },
    }
}

fn discriminant_overflow() {
    // Tests for https://github.com/rust-lang/rust/issues/62138.
    #[repr(u8)]
    #[allow(dead_code)]
    enum WithWraparoundInvalidValues {
        X = 1,
        Y = 254,
    }

    #[allow(dead_code)]
    enum Foo {
        A,
        B,
        C(WithWraparoundInvalidValues),
    }

    let x = Foo::B;
    if let Foo::C(_) = x {
        panic!();
    }
}

fn main() {
    test(MyEnum::MyEmptyVariant);
    test(MyEnum::MyNewtypeVariant(42));
    test(MyEnum::MyTupleVariant(43, 44));
    test(MyEnum::MyStructVariant{
        my_first_field: 45,
        my_second_field: 46,
    });

    discriminant_overflow();
}
<commit_msg>err on all-but-B, not just on C<commit_after>enum MyEnum {
    MyEmptyVariant,
    MyNewtypeVariant(i32),
    MyTupleVariant(i32, i32),
    MyStructVariant {
        my_first_field: i32,
        my_second_field: i32,
    }
}

fn test(me: MyEnum) {
    match me {
        MyEnum::MyEmptyVariant => {},
        MyEnum::MyNewtypeVariant(ref val) => assert_eq!(val, &42),
        MyEnum::MyTupleVariant(ref a, ref b) => {
            assert_eq!(a, &43);
            assert_eq!(b, &44);
        },
        MyEnum::MyStructVariant { ref my_first_field, ref my_second_field } => {
            assert_eq!(my_first_field, &45);
            assert_eq!(my_second_field, &46);
        },
    }
}

fn discriminant_overflow() {
    // Tests for https://github.com/rust-lang/rust/issues/62138.
    #[repr(u8)]
    #[allow(dead_code)]
    enum WithWraparoundInvalidValues {
        X = 1,
        Y = 254,
    }

    #[allow(dead_code)]
    enum Foo {
        A,
        B,
        C(WithWraparoundInvalidValues),
    }

    let x = Foo::B;
    match x {
        Foo::B => {},
        _ => panic!(),
    }
}

fn main() {
    test(MyEnum::MyEmptyVariant);
    test(MyEnum::MyNewtypeVariant(42));
    test(MyEnum::MyTupleVariant(43, 44));
    test(MyEnum::MyStructVariant{
        my_first_field: 45,
        my_second_field: 46,
    });

    discriminant_overflow();
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};

use failure::{bail, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use regex::Regex;

use crate::utils::cordova::CordovaConfig;
use crate::utils::vcs;
use crate::utils::xcode::InfoPlist;

pub fn get_cordova_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    let here = path.unwrap_or(env::current_dir()?);
    let platform = match here.file_name().and_then(OsStr::to_str) {
        Some("android") => "android",
        Some("ios") => "ios",
        _ => return Ok(None),
    };
    let base = match here.parent().and_then(Path::parent) {
        Some(path) => path,
        None => return Ok(None),
    };

    let path = base.join("config.xml");
    if_chain! {
        if let Ok(md) = path.metadata();
        if md.is_file();
        if let Ok(Some(config)) = CordovaConfig::load(path);
        then {
            match platform {
                "android" => Ok(Some(config.android_release_name())),
                "ios" => Ok(Some(config.ios_release_name())),
                _ => unreachable!(),
            }
        } else {
            Ok(None)
        }
    }
}

pub fn get_xcode_release_name(plist: Option<InfoPlist>) -> Result<Option<String>, Error> {
    // if we are executed from within xcode, then we can use the environment
    // based discovery to get a release name without any interpolation.
    if let Some(plist) = plist.or(InfoPlist::discover_from_env()?) {
        return Ok(Some(plist.get_release_name()));
    }

    Ok(None)
}

pub fn infer_gradle_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    lazy_static! {
        static ref APP_ID_RE: Regex = Regex::new(r#"applicationId\s+["']([^"']*)["']"#).unwrap();
        static ref VERSION_NAME_RE: Regex =
            Regex::new(r#"versionName\s+["']([^"']*)["']"#).unwrap();
    }

    let mut contents = String::new();
    let mut here = path.unwrap_or(env::current_dir()?);
    loop {
        if_chain! {
            if let Ok(build_md) = here.join("build.gradle").metadata();
            if build_md.is_file();
            if let Ok(app_md) = here.join("app/build.gradle").metadata();
            if app_md.is_file();
            if let Ok(mut f) = fs::File::open(here.join("app/build.gradle"));
            if f.read_to_string(&mut contents).is_ok();
            if let Some(app_id_caps) = APP_ID_RE.captures(&contents);
            if let Some(version_caps) = VERSION_NAME_RE.captures(&contents);
            then {
                return Ok(Some(format!("{}-{}", &app_id_caps[1], &version_caps[1])));
            }
        }
        if !here.pop() {
            break;
        }
    }

    Ok(None)
}

/// Detects the release name for the current working directory.
pub fn detect_release_name() -> Result<String, Error> {
    // cordova release detection first.
    if let Some(release) = get_cordova_release_name(None)? {
        return Ok(release);
    }

    // try heroku: https://docs.sentry.io/workflow/integrations/legacy-integrations/heroku/#configure-releases
    if let Ok(release) = env::var("HEROKU_SLUG_COMMIT") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // Heroku #2 https://devcenter.heroku.com/changelog-items/630
    if let Ok(release) = env::var("SOURCE_VERSION") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // for now only execute this on macs.  The reason is that this uses
    // xcodebuild which does not exist anywhere but there.
    if_chain! {
        if cfg!(target_os="macos");
        if let Some(release) = get_xcode_release_name(None)?;
        then {
            return Ok(release)
        }
    }

    // For android we badly parse gradle files.  We do this because most of the
    // time now people set the ids and versions in the gradle files instead of
    // the xml manifests.
    if let Some(release) = infer_gradle_release_name(None)? {
        return Ok(release);
    }

    if let Ok(head) = vcs::find_head() {
        Ok(head)
    } else {
        bail!("Could not automatically determine release name");
    }
}
<commit_msg>feat: Detect CodeBuild slug for propose-version (#681)<commit_after>use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};

use failure::{bail, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use regex::Regex;

use crate::utils::cordova::CordovaConfig;
use crate::utils::vcs;
use crate::utils::xcode::InfoPlist;

pub fn get_cordova_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    let here = path.unwrap_or(env::current_dir()?);
    let platform = match here.file_name().and_then(OsStr::to_str) {
        Some("android") => "android",
        Some("ios") => "ios",
        _ => return Ok(None),
    };
    let base = match here.parent().and_then(Path::parent) {
        Some(path) => path,
        None => return Ok(None),
    };

    let path = base.join("config.xml");
    if_chain! {
        if let Ok(md) = path.metadata();
        if md.is_file();
        if let Ok(Some(config)) = CordovaConfig::load(path);
        then {
            match platform {
                "android" => Ok(Some(config.android_release_name())),
                "ios" => Ok(Some(config.ios_release_name())),
                _ => unreachable!(),
            }
        } else {
            Ok(None)
        }
    }
}

pub fn get_xcode_release_name(plist: Option<InfoPlist>) -> Result<Option<String>, Error> {
    // if we are executed from within xcode, then we can use the environment
    // based discovery to get a release name without any interpolation.
    if let Some(plist) = plist.or(InfoPlist::discover_from_env()?) {
        return Ok(Some(plist.get_release_name()));
    }

    Ok(None)
}

pub fn infer_gradle_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    lazy_static! {
        static ref APP_ID_RE: Regex = Regex::new(r#"applicationId\s+["']([^"']*)["']"#).unwrap();
        static ref VERSION_NAME_RE: Regex =
            Regex::new(r#"versionName\s+["']([^"']*)["']"#).unwrap();
    }

    let mut contents = String::new();
    let mut here = path.unwrap_or(env::current_dir()?);
    loop {
        if_chain! {
            if let Ok(build_md) = here.join("build.gradle").metadata();
            if build_md.is_file();
            if let Ok(app_md) = here.join("app/build.gradle").metadata();
            if app_md.is_file();
            if let Ok(mut f) = fs::File::open(here.join("app/build.gradle"));
            if f.read_to_string(&mut contents).is_ok();
            if let Some(app_id_caps) = APP_ID_RE.captures(&contents);
            if let Some(version_caps) = VERSION_NAME_RE.captures(&contents);
            then {
                return Ok(Some(format!("{}-{}", &app_id_caps[1], &version_caps[1])));
            }
        }
        if !here.pop() {
            break;
        }
    }

    Ok(None)
}

/// Detects the release name for the current working directory.
pub fn detect_release_name() -> Result<String, Error> {
    // cordova release detection first.
    if let Some(release) = get_cordova_release_name(None)? {
        return Ok(release);
    }

    // try Heroku #1: https://docs.sentry.io/workflow/integrations/legacy-integrations/heroku/#configure-releases
    if let Ok(release) = env::var("HEROKU_SLUG_COMMIT") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // try Heroku #2 https://devcenter.heroku.com/changelog-items/630
    if let Ok(release) = env::var("SOURCE_VERSION") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // try AWS CodeBuild: https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
    if let Ok(release) = env::var("CODEBUILD_RESOLVED_SOURCE_VERSION") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // for now only execute this on macs.  The reason is that this uses
    // xcodebuild which does not exist anywhere but there.
    if_chain! {
        if cfg!(target_os="macos");
        if let Some(release) = get_xcode_release_name(None)?;
        then {
            return Ok(release)
        }
    }

    // For android we badly parse gradle files.  We do this because most of the
    // time now people set the ids and versions in the gradle files instead of
    // the xml manifests.
    if let Some(release) = infer_gradle_release_name(None)? {
        return Ok(release);
    }

    if let Ok(head) = vcs::find_head() {
        Ok(head)
    } else {
        bail!("Could not automatically determine release name");
    }
}
<|endoftext|>


<commit_before>//! Int array virtual table.
use std::cell::RefCell;
use std::default::Default;
use std::mem;
use std::rc::Rc;
use libc;

use {Connection, Error, Result};
use ffi;
use vtab::{declare_vtab, escape_double_quote, VTab, VTabCursor};

/// Create a specific instance of an intarray object.
/// The new intarray object is returned.
///
/// Each intarray object corresponds to a virtual table in the TEMP table
/// with the specified `name`.
pub fn create_int_array(conn: &Connection, name: &str) -> Result<Rc<RefCell<Vec<i64>>>> {
    let array = Rc::new(RefCell::new(Vec::new()));
    try!(conn.create_module(name, &INT_ARRAY_MODULE, Some(array.clone())));
    try!(conn.execute_batch(&format!("CREATE VIRTUAL TABLE temp.\"{0}\" USING \"{0}\"",
                                     escape_double_quote(name))));
    Ok(array)
}

/// Destroy the intarray object by dropping the virtual table.
/// If not done explicitly by the application, the virtual table will be dropped implicitly
/// by the system when the database connection is closed.
pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> {
    conn.execute_batch(&format!("DROP TABLE temp.\"{0}\"", escape_double_quote(name)))
}

init_module!(INT_ARRAY_MODULE, IntArrayVTab, IntArrayVTabCursor,
    int_array_create, int_array_best_index, int_array_destroy,
    int_array_open, int_array_close,
    int_array_filter, int_array_next, int_array_eof,
    int_array_column, int_array_rowid);

#[repr(C)]
struct IntArrayVTab {
    /// Base class
    base: ffi::sqlite3_vtab,
    array: *const Rc<RefCell<Vec<i64>>>,
}

impl VTab<IntArrayVTabCursor> for IntArrayVTab {
    fn create(db: *mut ffi::sqlite3,
              aux: *mut libc::c_void,
              _args: &[*const libc::c_char])
              -> Result<IntArrayVTab> {
        let array = unsafe { mem::transmute(aux) };
        let vtab = IntArrayVTab {
            base: Default::default(),
            array: array,
        };
        try!(declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"));
        Ok(vtab)
    }

    fn best_index(&self, _info: *mut ffi::sqlite3_index_info) {}

    fn open(&self) -> Result<IntArrayVTabCursor> {
        Ok(IntArrayVTabCursor::new())
    }
}

#[repr(C)]
struct IntArrayVTabCursor {
    /// Base class
    base: ffi::sqlite3_vtab_cursor,
    /// Current cursor position
    i: usize,
}

impl IntArrayVTabCursor {
    fn new() -> IntArrayVTabCursor {
        IntArrayVTabCursor {
            base: Default::default(),
            i: 0,
        }
    }
}

impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
    fn vtab(&self) -> &mut IntArrayVTab {
        unsafe { &mut *(self.base.pVtab as *mut IntArrayVTab) }
    }
    fn filter(&mut self,
              _idx_num: libc::c_int,
              _idx_str: *const libc::c_char,
              _argc: libc::c_int,
              _argv: *mut *mut ffi::sqlite3_value)
              -> Result<()> {
        self.i = 0;
        Ok(())
    }
    fn next(&mut self) -> Result<()> {
        self.i = self.i + 1;
        Ok(())
    }
    fn eof(&self) -> bool {
        let vtab = self.vtab();
        unsafe {
            let array = (*vtab.array).borrow();
            self.i >= array.len()
        }
    }
    fn column(&self, ctx: *mut ffi::sqlite3_context, _i: libc::c_int) -> Result<()> {
        use functions::ToResult;
        let vtab = self.vtab();
        unsafe {
            let array = (*vtab.array).borrow();
            array[self.i].set_result(ctx);
        }
        Ok(())
    }
    fn rowid(&self) -> Result<i64> {
        Ok(self.i as i64)
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use vtab::int_array;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_int_array_module() {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE t1 (x INT);
                INSERT INTO t1 VALUES (1), (3);
                CREATE TABLE t2 (y INT);
                INSERT INTO t2 VALUES (11);
                CREATE TABLE t3 (z INT);
                INSERT INTO t3 VALUES (-5);").unwrap();
        let p1 = int_array::create_int_array(&db, "ex1").unwrap();
        let p2 = int_array::create_int_array(&db, "ex2").unwrap();
        let p3 = int_array::create_int_array(&db, "ex3").unwrap();

        let mut s = db.prepare("SELECT * FROM t1, t2, t3
                WHERE t1.x IN ex1
                AND t2.y IN ex2
                AND t3.z IN ex3").unwrap();

        p1.borrow_mut().append(&mut vec![1, 2, 3, 4]);
        p2.borrow_mut().append(&mut vec![5, 6, 7, 8, 9, 10, 11]);
        p3.borrow_mut().append(&mut vec![-1, -5, -10]);

        {
            let rows = s.query(&[]).unwrap();
            for row in rows {
                let row = row.unwrap();
                let i1: i64 = row.get(0);
                assert!(i1 == 1 || i1 == 3);
                assert_eq!(11, row.get(1));
                assert_eq!(-5, row.get(2));
            }
        }

        s.reset_if_needed();
        p1.borrow_mut().clear();
        p2.borrow_mut().clear();
        p3.borrow_mut().clear();
        p1.borrow_mut().append(&mut vec![1]);
        p2.borrow_mut().append(&mut vec![7, 11]);
        p3.borrow_mut().append(&mut vec![-5, -10]);

        {
            let row = s.query(&[]).unwrap().next().unwrap().unwrap();
            assert_eq!(1, row.get(0));
            assert_eq!(11, row.get(1));
            assert_eq!(-5, row.get(2));
        }

        s.reset_if_needed();
        p2.borrow_mut().clear();
        p3.borrow_mut().clear();
        p2.borrow_mut().append(&mut vec![3, 4, 5]);
        p3.borrow_mut().append(&mut vec![0, -5]);
        assert!(s.query(&[]).unwrap().next().is_none());

        int_array::drop_int_array(&db, "ex1").unwrap();
        int_array::drop_int_array(&db, "ex2").unwrap();
        int_array::drop_int_array(&db, "ex3").unwrap();
    }
}
<commit_msg>Try to fix Travis build.<commit_after>//! Int array virtual table.
use std::cell::RefCell;
use std::default::Default;
use std::mem;
use std::rc::Rc;
use libc;

use {Connection, Error, Result};
use ffi;
use vtab::{declare_vtab, escape_double_quote, VTab, VTabCursor};

/// Create a specific instance of an intarray object.
/// The new intarray object is returned.
///
/// Each intarray object corresponds to a virtual table in the TEMP table
/// with the specified `name`.
pub fn create_int_array(conn: &Connection, name: &str) -> Result<Rc<RefCell<Vec<i64>>>> {
    let array = Rc::new(RefCell::new(Vec::new()));
    try!(conn.create_module(name, &INT_ARRAY_MODULE, Some(array.clone())));
    try!(conn.execute_batch(&format!("CREATE VIRTUAL TABLE temp.\"{0}\" USING \"{0}\"",
                                     escape_double_quote(name))));
    Ok(array)
}

/// Destroy the intarray object by dropping the virtual table.
/// If not done explicitly by the application, the virtual table will be dropped implicitly
/// by the system when the database connection is closed.
pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> {
    conn.execute_batch(&format!("DROP TABLE temp.\"{0}\"", escape_double_quote(name)))
}

init_module!(INT_ARRAY_MODULE, IntArrayVTab, IntArrayVTabCursor,
    int_array_create, int_array_best_index, int_array_destroy,
    int_array_open, int_array_close,
    int_array_filter, int_array_next, int_array_eof,
    int_array_column, int_array_rowid);

#[repr(C)]
struct IntArrayVTab {
    /// Base class
    base: ffi::sqlite3_vtab,
    array: *const Rc<RefCell<Vec<i64>>>,
}

impl VTab<IntArrayVTabCursor> for IntArrayVTab {
    fn create(db: *mut ffi::sqlite3,
              aux: *mut libc::c_void,
              _args: &[*const libc::c_char])
              -> Result<IntArrayVTab> {
        let array = unsafe { mem::transmute(aux) };
        let vtab = IntArrayVTab {
            base: Default::default(),
            array: array,
        };
        try!(declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"));
        Ok(vtab)
    }

    fn best_index(&self, _info: *mut ffi::sqlite3_index_info) {}

    fn open(&self) -> Result<IntArrayVTabCursor> {
        Ok(IntArrayVTabCursor::new())
    }
}

#[repr(C)]
struct IntArrayVTabCursor {
    /// Base class
    base: ffi::sqlite3_vtab_cursor,
    /// Current cursor position
    i: usize,
}

impl IntArrayVTabCursor {
    fn new() -> IntArrayVTabCursor {
        IntArrayVTabCursor {
            base: Default::default(),
            i: 0,
        }
    }
}

impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
    fn vtab(&self) -> &mut IntArrayVTab {
        unsafe { &mut *(self.base.pVtab as *mut IntArrayVTab) }
    }
    fn filter(&mut self,
              _idx_num: libc::c_int,
              _idx_str: *const libc::c_char,
              _argc: libc::c_int,
              _argv: *mut *mut ffi::sqlite3_value)
              -> Result<()> {
        self.i = 0;
        Ok(())
    }
    fn next(&mut self) -> Result<()> {
        self.i = self.i + 1;
        Ok(())
    }
    fn eof(&self) -> bool {
        let vtab = self.vtab();
        unsafe {
            let array = (*vtab.array).borrow();
            self.i >= array.len()
        }
    }
    fn column(&self, ctx: *mut ffi::sqlite3_context, _i: libc::c_int) -> Result<()> {
        use functions::ToResult;
        let vtab = self.vtab();
        unsafe {
            let array = (*vtab.array).borrow();
            array[self.i].set_result(ctx);
        }
        Ok(())
    }
    fn rowid(&self) -> Result<i64> {
        Ok(self.i as i64)
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use vtab::int_array;

    #[test]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    fn test_int_array_module() {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE t1 (x INT);
                INSERT INTO t1 VALUES (1);
                INSERT INTO t1 VALUES (3);
                CREATE TABLE t2 (y INT);
                INSERT INTO t2 VALUES (11);
                CREATE TABLE t3 (z INT);
                INSERT INTO t3 VALUES (-5);").unwrap();
        let p1 = int_array::create_int_array(&db, "ex1").unwrap();
        let p2 = int_array::create_int_array(&db, "ex2").unwrap();
        let p3 = int_array::create_int_array(&db, "ex3").unwrap();

        let mut s = db.prepare("SELECT * FROM t1, t2, t3
                WHERE t1.x IN ex1
                AND t2.y IN ex2
                AND t3.z IN ex3").unwrap();

        p1.borrow_mut().append(&mut vec![1, 2, 3, 4]);
        p2.borrow_mut().append(&mut vec![5, 6, 7, 8, 9, 10, 11]);
        p3.borrow_mut().append(&mut vec![-1, -5, -10]);

        {
            let rows = s.query(&[]).unwrap();
            for row in rows {
                let row = row.unwrap();
                let i1: i64 = row.get(0);
                assert!(i1 == 1 || i1 == 3);
                assert_eq!(11, row.get(1));
                assert_eq!(-5, row.get(2));
            }
        }

        s.reset_if_needed();
        p1.borrow_mut().clear();
        p2.borrow_mut().clear();
        p3.borrow_mut().clear();
        p1.borrow_mut().append(&mut vec![1]);
        p2.borrow_mut().append(&mut vec![7, 11]);
        p3.borrow_mut().append(&mut vec![-5, -10]);

        {
            let row = s.query(&[]).unwrap().next().unwrap().unwrap();
            assert_eq!(1, row.get(0));
            assert_eq!(11, row.get(1));
            assert_eq!(-5, row.get(2));
        }

        s.reset_if_needed();
        p2.borrow_mut().clear();
        p3.borrow_mut().clear();
        p2.borrow_mut().append(&mut vec![3, 4, 5]);
        p3.borrow_mut().append(&mut vec![0, -5]);
        assert!(s.query(&[]).unwrap().next().is_none());

        int_array::drop_int_array(&db, "ex1").unwrap();
        int_array::drop_int_array(&db, "ex2").unwrap();
        int_array::drop_int_array(&db, "ex3").unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use libc;
use parking_lot::{Condvar, Mutex};

use super::*;
use super::utils::mpv_err;
use super::super::LogLevel;
use super::super::raw::*;
use super::super::raw::prototype::*;

use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem;
use std::ffi::{CStr, CString};

pub(crate) unsafe extern "C" fn event_callback(d: *mut libc::c_void) {
    (*(d as *mut (Mutex<bool>, Condvar))).1.notify_one();
}

#[doc(hidden)]
#[derive(Clone, Debug, PartialEq)]
/// Designed for internal use.
pub struct InnerEvent {
    event: Event,
    err: Option<Error>,
}

impl InnerEvent {
    fn as_result(&self) -> Result<Event, Error> {
        if self.err.is_some() {
            Err(self.err.clone().unwrap())
        } else {
            Ok(self.event.clone())
        }
    }
    fn as_event(&self) -> &Event {
        &self.event
    }
    pub(crate) fn as_id(&self) -> MpvEventId {
        self.event.as_id()
    }
}

/// An event returned by `EventIter`.
#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)]
pub enum Event {
    LogMessage(LogMessage),
    StartFile,
    EndFile(Option<EndFile>),
    FileLoaded,
    Idle,
    Tick,
    VideoReconfig,
    AudioReconfig,
    Seek,
    PlaybackRestart,
    PropertyChange(Property),
}

impl Event {
    pub(crate) fn as_id(&self) -> MpvEventId {
        match *self {
            Event::LogMessage(_) => MpvEventId::LogMessage,
            Event::StartFile => MpvEventId::StartFile,
            Event::EndFile(_) => MpvEventId::EndFile,
            Event::FileLoaded => MpvEventId::FileLoaded,
            Event::Idle => MpvEventId::Idle,
            Event::Tick => MpvEventId::Tick,
            Event::VideoReconfig => MpvEventId::VideoReconfig,
            Event::AudioReconfig => MpvEventId::AudioReconfig,
            Event::Seek => MpvEventId::Seek,
            Event::PlaybackRestart => MpvEventId::PlaybackRestart,
            Event::PropertyChange(_) => MpvEventId::PropertyChange,
        }
    }
}

impl MpvEvent {
    fn as_event(&self) -> Result<Event, Error> {
        try!(mpv_err((), self.error));
        Ok(match self.event_id {
            MpvEventId::LogMessage => Event::LogMessage(LogMessage::from_raw(self.data)),
            MpvEventId::StartFile => Event::StartFile,
            MpvEventId::EndFile => {
                Event::EndFile(Some(EndFile::from_raw(MpvEventEndFile::from_raw(self.data))))
            }
            MpvEventId::FileLoaded => Event::FileLoaded,
            MpvEventId::Idle => Event::Idle,
            MpvEventId::Tick => Event::Tick,
            MpvEventId::VideoReconfig => Event::VideoReconfig,
            MpvEventId::AudioReconfig => Event::AudioReconfig,
            MpvEventId::Seek => Event::Seek,
            MpvEventId::PlaybackRestart => Event::PlaybackRestart,
            MpvEventId::PropertyChange => Event::PropertyChange(Property::from_raw(self.data)),
            _ => unreachable!(),
        })
    }
    fn as_inner_event(&self) -> InnerEvent {
        InnerEvent {
            event: match self.event_id {
                MpvEventId::LogMessage => Event::LogMessage(LogMessage::from_raw(self.data)),
                MpvEventId::StartFile => Event::StartFile,
                MpvEventId::EndFile => {
                    Event::EndFile(Some(EndFile::from_raw(MpvEventEndFile::from_raw(self.data))))
                }
                MpvEventId::FileLoaded => Event::FileLoaded,
                MpvEventId::Idle => Event::Idle,
                MpvEventId::Tick => Event::Tick,
                MpvEventId::VideoReconfig => Event::VideoReconfig,
                MpvEventId::AudioReconfig => Event::AudioReconfig,
                MpvEventId::Seek => Event::Seek,
                MpvEventId::PlaybackRestart => Event::PlaybackRestart,
                MpvEventId::PropertyChange => Event::PropertyChange(Property::from_raw(self.data)),
                _ => unreachable!(),
            },
            err: {
                let err = mpv_err((), self.error);
                if err.is_err() {
                    Some(err.unwrap_err())
                } else {
                    None
                }
            },
        }
    }
}

#[derive(Debug)]
/// A blocking `Iterator` over some observed events of an mpv instance.
/// Once the `EventIter` is dropped, it's `Event`s are removed from
/// the "to be observed" queue, therefore new `Event` invocations won't be observed.
pub struct EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    pub(crate) ctx: *mut MpvHandle,
    pub(crate) first_iteration: bool,
    pub(crate) notification: *mut (Mutex<bool>, Condvar),
    pub(crate) all_to_observe: &'parent Mutex<Vec<Event>>,
    pub(crate) all_to_observe_properties: &'parent Mutex<HashMap<String, libc::uint64_t>>,
    pub(crate) local_to_observe: Vec<Event>,
    pub(crate) all_observed: &'parent Mutex<Vec<InnerEvent>>,
    pub(crate) _does_not_outlive: PhantomData<&'parent P>,
}

impl<'parent, P> Drop for EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    fn drop(&mut self) {
        let mut all_to_observe = self.all_to_observe.lock();
        let mut all_observed = self.all_observed.lock();
        let mut all_to_observe_properties = self.all_to_observe_properties.lock();

        // Returns true if outer and inner event match, in the case of the event
        // being a property unobserve it.
        let mut compare_ev_unobserve = |outer_ev: &Event, inner_ev: &Event| {
            if let Event::PropertyChange(ref outer_prop) = *outer_ev {
                if let Event::PropertyChange(ref inner_prop) = *inner_ev {
                    if outer_prop.name == inner_prop.name {
                        unsafe {
                            mpv_unobserve_property(self.ctx, all_to_observe_properties.remove(
                                                                        &outer_prop.name).unwrap());
                        }
                        return true;
                    }
                } else if MpvEventId::LogMessage == outer_ev.as_id() &&
                   outer_ev.as_id() == inner_ev.as_id() {
                    let min_level = CString::new(MpvLogLevel::None.as_string()).unwrap();
                    mpv_err((),
                            unsafe { mpv_request_log_messages(self.ctx, min_level.as_ptr()) })
                        .unwrap();
                        return true;
                }
                unsafe { mpv_request_event(self.ctx, inner_ev.as_id(), 0) };
                return true;
            } else if outer_ev.as_id() == inner_ev.as_id() {
                unsafe { mpv_request_event(self.ctx, inner_ev.as_id(), 0) };
                return true;
            }
            false
        };

        // This removes all events for which compare_ev_unobserve returns true.
        for outer_ev in &self.local_to_observe {
            all_to_observe.retain(|inner_ev| !compare_ev_unobserve(outer_ev, inner_ev));
            all_observed.retain(|inner_ev| !compare_ev_unobserve(outer_ev, (*inner_ev).as_event()));
        }
    }
}

impl<'parent, P> Iterator for EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    type Item = Vec<Result<Event, Error>>;

    fn next(&mut self) -> Option<Self::Item> {
        'no_events_anchor: loop {
            let mut observed = self.all_observed.lock();
            if observed.is_empty() && !self.first_iteration {
                mem::drop(observed);
                unsafe { (*self.notification).1.wait(&mut (*self.notification).0.lock()) };
                observed = self.all_observed.lock();
            }

            let mut ret_events = Vec::with_capacity(observed.len());
            if observed.is_empty() || self.first_iteration {
                let all_to_observe = self.all_to_observe.lock();
                let mut last = false;
                'events: loop {
                    let event = unsafe { &*mpv_wait_event(self.ctx, 0f32 as _) };
                    let ev_id = event.event_id;

                    if ev_id == MpvEventId::QueueOverflow {
                        // The queue needs to be emptied asap to prevent loss of events
                        break;
                    } else if ev_id == MpvEventId::None {
                        if last {
                            break;
                        } else {
                            last = true;
                            continue;
                        }
                    }
                    for local_ob_ev in &self.local_to_observe {
                        if ev_id == local_ob_ev.as_id() {
                            ret_events.push(event.as_event());
                            continue 'events;
                        }
                    }
                    for all_ob_ev in &*all_to_observe {
                        if ev_id == all_ob_ev.as_id() {
                            observed.push(event.as_inner_event());
                            continue 'events;
                        }
                    }
                }
                if !observed.is_empty() {
                    mem::drop(observed);
                    unsafe { (*self.notification).1.notify_all() };
                }
            } else {
                // Return true where outer_ev == inner_ev, and push inner_ev to ret_events
                let mut compare_ev = |outer_ev: &Event, inner_ev: &InnerEvent| {
                    if let Event::PropertyChange(ref outer_prop) = *outer_ev {
                        if let Event::PropertyChange(ref inner_prop) = *inner_ev.as_event() {
                            if outer_prop.name == inner_prop.name {
                                ret_events.push(inner_ev.as_result());
                                return true;
                            }
                        }
                        ret_events.push(inner_ev.as_result());
                        return true;
                    } else if outer_ev.as_id() == inner_ev.as_id() {
                        ret_events.push(inner_ev.as_result());
                        return true;
                    }
                    false
                };
                // Remove events belonging to this EventIter from observed
                for outer_ev in &self.local_to_observe {
                    observed.retain(|inner_ev| !compare_ev(outer_ev, inner_ev));
                }

                if !observed.is_empty() {
                    mem::drop(observed);
                    unsafe { (*self.notification).1.notify_all() };
                }
            }

            self.first_iteration = false;

            if !ret_events.is_empty() {
                return Some(ret_events);
            } else {
                continue 'no_events_anchor;
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
/// The data of an `Event::LogMessage`.
pub struct LogMessage {
    pub prefix: String,
    pub level: LogLevel,
    pub text: String,
    pub log_level: LogLevel,
}

impl LogMessage {
    /// Create an empty `LogMessage` with specified verbosity, useful for observing.
    pub fn new(lvl: LogLevel) -> LogMessage {
        LogMessage {
            prefix: "".into(),
            level: lvl,
            text: "".into(),
            log_level: lvl,
        }
    }

    fn from_raw(raw: *mut libc::c_void) -> LogMessage {
        debug_assert!(!raw.is_null());
        let raw = unsafe { &mut *(raw as *mut MpvEventLogMessage) };
        LogMessage {
            prefix: unsafe { CStr::from_ptr(raw.prefix).to_str().unwrap().into() },
            level: unsafe { MpvLogLevel::from_str(CStr::from_ptr(raw.level).to_str().unwrap()) },
            text: unsafe { CStr::from_ptr(raw.text).to_str().unwrap().into() },
            log_level: raw.log_level,
        }
    }
}

impl MpvLogLevel {
    pub(crate) fn as_string(&self) -> &str {
        match *self {
            MpvLogLevel::None => "no",
            MpvLogLevel::Fatal => "fatal",
            MpvLogLevel::Error => "error",
            MpvLogLevel::Warn => "warn",
            MpvLogLevel::Info => "info",
            MpvLogLevel::V => "v",
            MpvLogLevel::Debug => "debug",
            MpvLogLevel::Trace => "trace",
        }
    }

    fn from_str(name: &str) -> MpvLogLevel {
        match name {
            "no" => MpvLogLevel::None,
            "fatal" => MpvLogLevel::Fatal,
            "error" => MpvLogLevel::Error,
            "warn" => MpvLogLevel::Warn,
            "info" => MpvLogLevel::Info,
            "v" => MpvLogLevel::V,
            "debug" => MpvLogLevel::Debug,
            "trace" => MpvLogLevel::Trace,
            _ => unreachable!(),
        }
    }
}

impl MpvEventEndFile {
    fn from_raw(raw: *mut libc::c_void) -> MpvEventEndFile {
        debug_assert!(!raw.is_null());
        let raw = unsafe { &mut *(raw as *mut MpvEventEndFile) };
        MpvEventEndFile {
            reason: raw.reason,
            error: raw.error,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
/// The reason an `Event::EndFile` was fired.
pub enum EndFileReason {
    Eof = 0,
    Stop = 2,
    Quit = 3,
    Error = 4,
    Redirect = 5,
}

#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)]
/// The data of an `Event::EndFile`. `error` is `Some` if `EndFileReason` is `Error`.
pub struct EndFile {
    pub reason: EndFileReason,
    pub error: Option<Error>,
}

impl EndFile {
    fn from_raw(raw: MpvEventEndFile) -> EndFile {
        EndFile {
            reason: match raw.reason {
                0 => EndFileReason::Eof,
                2 => EndFileReason::Stop,
                3 => EndFileReason::Quit,
                4 => EndFileReason::Error,
                5 => EndFileReason::Redirect,
                _ => unreachable!(),

            },
            error: {
                let err = mpv_err((), raw.error);
                if err.is_ok() {
                    None
                } else {
                    Some(err.unwrap_err())
                }
            },
        }
    }
}
<commit_msg>Fix wrong matching of events in EventIter<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use libc;
use parking_lot::{Condvar, Mutex};

use super::*;
use super::utils::mpv_err;
use super::super::LogLevel;
use super::super::raw::*;
use super::super::raw::prototype::*;

use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem;
use std::ffi::{CStr, CString};

pub(crate) unsafe extern "C" fn event_callback(d: *mut libc::c_void) {
    (*(d as *mut (Mutex<bool>, Condvar))).1.notify_one();
}

#[doc(hidden)]
#[derive(Clone, Debug, PartialEq)]
/// Designed for internal use.
pub struct InnerEvent {
    event: Event,
    err: Option<Error>,
}

impl InnerEvent {
    fn as_result(&self) -> Result<Event, Error> {
        if self.err.is_some() {
            Err(self.err.clone().unwrap())
        } else {
            Ok(self.event.clone())
        }
    }
    fn as_event(&self) -> &Event {
        &self.event
    }
    pub(crate) fn as_id(&self) -> MpvEventId {
        self.event.as_id()
    }
}

/// An event returned by `EventIter`.
#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)]
pub enum Event {
    LogMessage(LogMessage),
    StartFile,
    EndFile(Option<EndFile>),
    FileLoaded,
    Idle,
    Tick,
    VideoReconfig,
    AudioReconfig,
    Seek,
    PlaybackRestart,
    PropertyChange(Property),
}

impl Event {
    pub(crate) fn as_id(&self) -> MpvEventId {
        match *self {
            Event::LogMessage(_) => MpvEventId::LogMessage,
            Event::StartFile => MpvEventId::StartFile,
            Event::EndFile(_) => MpvEventId::EndFile,
            Event::FileLoaded => MpvEventId::FileLoaded,
            Event::Idle => MpvEventId::Idle,
            Event::Tick => MpvEventId::Tick,
            Event::VideoReconfig => MpvEventId::VideoReconfig,
            Event::AudioReconfig => MpvEventId::AudioReconfig,
            Event::Seek => MpvEventId::Seek,
            Event::PlaybackRestart => MpvEventId::PlaybackRestart,
            Event::PropertyChange(_) => MpvEventId::PropertyChange,
        }
    }
}

impl MpvEvent {
    fn as_event(&self) -> Result<Event, Error> {
        try!(mpv_err((), self.error));
        Ok(match self.event_id {
            MpvEventId::LogMessage => Event::LogMessage(LogMessage::from_raw(self.data)),
            MpvEventId::StartFile => Event::StartFile,
            MpvEventId::EndFile => {
                Event::EndFile(Some(EndFile::from_raw(MpvEventEndFile::from_raw(self.data))))
            }
            MpvEventId::FileLoaded => Event::FileLoaded,
            MpvEventId::Idle => Event::Idle,
            MpvEventId::Tick => Event::Tick,
            MpvEventId::VideoReconfig => Event::VideoReconfig,
            MpvEventId::AudioReconfig => Event::AudioReconfig,
            MpvEventId::Seek => Event::Seek,
            MpvEventId::PlaybackRestart => Event::PlaybackRestart,
            MpvEventId::PropertyChange => Event::PropertyChange(Property::from_raw(self.data)),
            _ => unreachable!(),
        })
    }
    fn as_inner_event(&self) -> InnerEvent {
        InnerEvent {
            event: match self.event_id {
                MpvEventId::LogMessage => Event::LogMessage(LogMessage::from_raw(self.data)),
                MpvEventId::StartFile => Event::StartFile,
                MpvEventId::EndFile => {
                    Event::EndFile(Some(EndFile::from_raw(MpvEventEndFile::from_raw(self.data))))
                }
                MpvEventId::FileLoaded => Event::FileLoaded,
                MpvEventId::Idle => Event::Idle,
                MpvEventId::Tick => Event::Tick,
                MpvEventId::VideoReconfig => Event::VideoReconfig,
                MpvEventId::AudioReconfig => Event::AudioReconfig,
                MpvEventId::Seek => Event::Seek,
                MpvEventId::PlaybackRestart => Event::PlaybackRestart,
                MpvEventId::PropertyChange => Event::PropertyChange(Property::from_raw(self.data)),
                _ => unreachable!(),
            },
            err: {
                let err = mpv_err((), self.error);
                if err.is_err() {
                    Some(err.unwrap_err())
                } else {
                    None
                }
            },
        }
    }
}

#[derive(Debug)]
/// A blocking `Iterator` over some observed events of an mpv instance.
/// Once the `EventIter` is dropped, it's `Event`s are removed from
/// the "to be observed" queue, therefore new `Event` invocations won't be observed.
pub struct EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    pub(crate) ctx: *mut MpvHandle,
    pub(crate) first_iteration: bool,
    pub(crate) notification: *mut (Mutex<bool>, Condvar),
    pub(crate) all_to_observe: &'parent Mutex<Vec<Event>>,
    pub(crate) all_to_observe_properties: &'parent Mutex<HashMap<String, libc::uint64_t>>,
    pub(crate) local_to_observe: Vec<Event>,
    pub(crate) all_observed: &'parent Mutex<Vec<InnerEvent>>,
    pub(crate) _does_not_outlive: PhantomData<&'parent P>,
}

impl<'parent, P> Drop for EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    fn drop(&mut self) {
        let mut all_to_observe = self.all_to_observe.lock();
        let mut all_observed = self.all_observed.lock();
        let mut all_to_observe_properties = self.all_to_observe_properties.lock();

        // Returns true if outer and inner event match, in the case of the event
        // being a property unobserve it.
        let mut compare_ev_unobserve = |outer_ev: &Event, inner_ev: &Event| {
            if let Event::PropertyChange(ref outer_prop) = *outer_ev {
                if let Event::PropertyChange(ref inner_prop) = *inner_ev {
                    if outer_prop.name == inner_prop.name {
                        unsafe {
                            mpv_unobserve_property(self.ctx, all_to_observe_properties.remove(
                                                                        &outer_prop.name).unwrap());
                        }
                        return true;
                    }
                } else if MpvEventId::LogMessage == outer_ev.as_id() &&
                   outer_ev.as_id() == inner_ev.as_id() {
                    let min_level = CString::new(MpvLogLevel::None.as_string()).unwrap();
                    mpv_err((),
                            unsafe { mpv_request_log_messages(self.ctx, min_level.as_ptr()) })
                        .unwrap();
                        return true;
                }
            } else if outer_ev.as_id() == inner_ev.as_id() {
                unsafe { mpv_request_event(self.ctx, inner_ev.as_id(), 0) };
                return true;
            }
            false
        };

        // This removes all events for which compare_ev_unobserve returns true.
        for outer_ev in &self.local_to_observe {
            all_to_observe.retain(|inner_ev| !compare_ev_unobserve(outer_ev, inner_ev));
            all_observed.retain(|inner_ev| !compare_ev_unobserve(outer_ev, (*inner_ev).as_event()));
        }
    }
}

impl<'parent, P> Iterator for EventIter<'parent, P>
    where P: MpvMarker + 'parent
{
    type Item = Vec<Result<Event, Error>>;

    fn next(&mut self) -> Option<Self::Item> {
        'no_events_anchor: loop {
            let mut observed = self.all_observed.lock();
            if observed.is_empty() && !self.first_iteration {
                mem::drop(observed);
                unsafe { (*self.notification).1.wait(&mut (*self.notification).0.lock()) };
                observed = self.all_observed.lock();
            }

            let mut ret_events = Vec::with_capacity(observed.len());
            if observed.is_empty() || self.first_iteration {
                let all_to_observe = self.all_to_observe.lock();
                let mut last = false;
                'events: loop {
                    let event = unsafe { &*mpv_wait_event(self.ctx, 0f32 as _) };
                    let ev_id = event.event_id;

                    if ev_id == MpvEventId::QueueOverflow {
                        // The queue needs to be emptied asap to prevent loss of events
                        break;
                    } else if ev_id == MpvEventId::None {
                        if last {
                            break;
                        } else {
                            last = true;
                            continue;
                        }
                    }
                    for local_ob_ev in &self.local_to_observe {
                        if ev_id == local_ob_ev.as_id() {
                            ret_events.push(event.as_event());
                            continue 'events;
                        }
                    }
                    for all_ob_ev in &*all_to_observe {
                        if ev_id == all_ob_ev.as_id() {
                            observed.push(event.as_inner_event());
                            continue 'events;
                        }
                    }
                }
                if !observed.is_empty() {
                    mem::drop(observed);
                    unsafe { (*self.notification).1.notify_all() };
                }
            } else {
                // Return true where outer_ev == inner_ev, and push inner_ev to ret_events
                let mut compare_ev = |outer_ev: &Event, inner_ev: &InnerEvent| {
                    if let Event::PropertyChange(ref outer_prop) = *outer_ev {
                        if let Event::PropertyChange(ref inner_prop) = *inner_ev.as_event() {
                            if outer_prop.name == inner_prop.name {
                                ret_events.push(inner_ev.as_result());
                                return true;
                            }
                        }
                    } else if outer_ev.as_id() == inner_ev.as_id() {
                        ret_events.push(inner_ev.as_result());
                        return true;
                    }
                    false
                };
                // Remove events belonging to this EventIter from observed
                for outer_ev in &self.local_to_observe {
                    observed.retain(|inner_ev| !compare_ev(outer_ev, inner_ev));
                }

                if !observed.is_empty() {
                    mem::drop(observed);
                    unsafe { (*self.notification).1.notify_all() };
                }
            }

            self.first_iteration = false;

            if !ret_events.is_empty() {
                return Some(ret_events);
            } else {
                continue 'no_events_anchor;
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
/// The data of an `Event::LogMessage`.
pub struct LogMessage {
    pub prefix: String,
    pub level: LogLevel,
    pub text: String,
    pub log_level: LogLevel,
}

impl LogMessage {
    /// Create an empty `LogMessage` with specified verbosity, useful for observing.
    pub fn new(lvl: LogLevel) -> LogMessage {
        LogMessage {
            prefix: "".into(),
            level: lvl,
            text: "".into(),
            log_level: lvl,
        }
    }

    fn from_raw(raw: *mut libc::c_void) -> LogMessage {
        debug_assert!(!raw.is_null());
        let raw = unsafe { &mut *(raw as *mut MpvEventLogMessage) };
        LogMessage {
            prefix: unsafe { CStr::from_ptr(raw.prefix).to_str().unwrap().into() },
            level: unsafe { MpvLogLevel::from_str(CStr::from_ptr(raw.level).to_str().unwrap()) },
            text: unsafe { CStr::from_ptr(raw.text).to_str().unwrap().into() },
            log_level: raw.log_level,
        }
    }
}

impl MpvLogLevel {
    pub(crate) fn as_string(&self) -> &str {
        match *self {
            MpvLogLevel::None => "no",
            MpvLogLevel::Fatal => "fatal",
            MpvLogLevel::Error => "error",
            MpvLogLevel::Warn => "warn",
            MpvLogLevel::Info => "info",
            MpvLogLevel::V => "v",
            MpvLogLevel::Debug => "debug",
            MpvLogLevel::Trace => "trace",
        }
    }

    fn from_str(name: &str) -> MpvLogLevel {
        match name {
            "no" => MpvLogLevel::None,
            "fatal" => MpvLogLevel::Fatal,
            "error" => MpvLogLevel::Error,
            "warn" => MpvLogLevel::Warn,
            "info" => MpvLogLevel::Info,
            "v" => MpvLogLevel::V,
            "debug" => MpvLogLevel::Debug,
            "trace" => MpvLogLevel::Trace,
            _ => unreachable!(),
        }
    }
}

impl MpvEventEndFile {
    fn from_raw(raw: *mut libc::c_void) -> MpvEventEndFile {
        debug_assert!(!raw.is_null());
        let raw = unsafe { &mut *(raw as *mut MpvEventEndFile) };
        MpvEventEndFile {
            reason: raw.reason,
            error: raw.error,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
/// The reason an `Event::EndFile` was fired.
pub enum EndFileReason {
    Eof = 0,
    Stop = 2,
    Quit = 3,
    Error = 4,
    Redirect = 5,
}

#[derive(Clone, Debug, PartialEq)]
#[allow(missing_docs)]
/// The data of an `Event::EndFile`. `error` is `Some` if `EndFileReason` is `Error`.
pub struct EndFile {
    pub reason: EndFileReason,
    pub error: Option<Error>,
}

impl EndFile {
    fn from_raw(raw: MpvEventEndFile) -> EndFile {
        EndFile {
            reason: match raw.reason {
                0 => EndFileReason::Eof,
                2 => EndFileReason::Stop,
                3 => EndFileReason::Quit,
                4 => EndFileReason::Error,
                5 => EndFileReason::Redirect,
                _ => unreachable!(),

            },
            error: {
                let err = mpv_err((), raw.error);
                if err.is_ok() {
                    None
                } else {
                    Some(err.unwrap_err())
                }
            },
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use libc::c_int;

// Equivalent of FFmpeg's `FFERRTAG` macro to generate error codes.
#[allow(non_snake_case)]
const fn FFERRTAG(tag: &[u8; 4]) -> c_int {
    -(tag[0] as c_int | (tag[1] as c_int) << 8 | (tag[2] as c_int) << 16 | (tag[3] as c_int) << 24)
}

pub const AVERROR_EOF: c_int = FFERRTAG(b"EOF ");
pub const AVERROR_INVALIDDATA: c_int = FFERRTAG(b"INDA");
pub const AVERROR_EAGAIN: c_int = -11;

#[cfg(test)]
mod test {
    use libc::c_char;

    use super::*;
    use crate::ffi;

    fn test_averror(averror: c_int, expected_message: &str) {
        let mut buffer = [0u8; 255];
        let ret = unsafe {
            ffi::av_strerror(
                averror,
                buffer.as_mut_ptr() as *mut c_char,
                buffer.len() as ffi::size_t,
            )
        };
        assert_eq!(ret, 0);

        let end_of_string = buffer.iter().position(|i| *i == 0).unwrap_or(buffer.len());
        let error_string = std::str::from_utf8(&buffer[0..end_of_string]).unwrap();
        assert_eq!(error_string, expected_message);
    }

    #[test]
    // Check that we got our error bindings correctly.
    fn test_error_bindings() {
        test_averror(AVERROR_EOF, "End of file");
        test_averror(
            AVERROR_INVALIDDATA,
            "Invalid data found when processing input",
        );
        test_averror(AVERROR_EAGAIN, "Resource temporarily unavailable");
    }
}
<commit_msg>media: ffmpeg: Fix test_averror not compiling.<commit_after>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use libc::c_int;

// Equivalent of FFmpeg's `FFERRTAG` macro to generate error codes.
#[allow(non_snake_case)]
const fn FFERRTAG(tag: &[u8; 4]) -> c_int {
    -(tag[0] as c_int | (tag[1] as c_int) << 8 | (tag[2] as c_int) << 16 | (tag[3] as c_int) << 24)
}

pub const AVERROR_EOF: c_int = FFERRTAG(b"EOF ");
pub const AVERROR_INVALIDDATA: c_int = FFERRTAG(b"INDA");
pub const AVERROR_EAGAIN: c_int = -11;

#[cfg(test)]
mod test {
    use libc::c_char;

    use super::*;
    use crate::ffi;

    fn test_averror(averror: c_int, expected_message: &str) {
        let mut buffer = [0u8; 255];
        let ret = unsafe {
            ffi::av_strerror(
                averror,
                buffer.as_mut_ptr() as *mut c_char,
                buffer.len() as usize,
            )
        };
        assert_eq!(ret, 0);

        let end_of_string = buffer.iter().position(|i| *i == 0).unwrap_or(buffer.len());
        let error_string = std::str::from_utf8(&buffer[0..end_of_string]).unwrap();
        assert_eq!(error_string, expected_message);
    }

    #[test]
    // Check that we got our error bindings correctly.
    fn test_error_bindings() {
        test_averror(AVERROR_EOF, "End of file");
        test_averror(
            AVERROR_INVALIDDATA,
            "Invalid data found when processing input",
        );
        test_averror(AVERROR_EAGAIN, "Resource temporarily unavailable");
    }
}
<|endoftext|>


<commit_before>//! Test metadata fetch, group membership, consumer metadata.
extern crate env_logger;
extern crate futures;
extern crate rand;
extern crate rdkafka;

use futures::*;

use rdkafka::consumer::Consumer;
use rdkafka::topic_partition_list::TopicPartitionList;

mod utils;
use utils::*;

#[test]
fn test_metadata() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(0), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(1), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(2), None);
    let consumer = create_stream_consumer(&rand_test_group(), None);

    let metadata = consumer.fetch_metadata(None, 5000).unwrap();
    assert_eq!(metadata.orig_broker_id(), -1);
    assert_eq!(metadata.orig_broker_name(), "localhost:9092/bootstrap");

    let broker_metadata = metadata.brokers();
    assert_eq!(broker_metadata.len(), 1);
    assert_eq!(broker_metadata[0].id(), 0);
    assert!(broker_metadata[0].host().len() > 0);
    assert_eq!(broker_metadata[0].port(), 9092);

    let topic_metadata = metadata.topics().iter()
        .find(|m| m.name() == topic_name).unwrap();

    let mut ids = topic_metadata.partitions().iter()
        .map(|p| {
            assert_eq!(p.error(), None);
            p.id()
        })
        .collect::<Vec<_>>();
    ids.sort();

    assert_eq!(ids, vec![0, 1, 2]);
    assert_eq!(topic_metadata.error(), None);
    assert_eq!(topic_metadata.partitions().len(), 3);
    assert_eq!(topic_metadata.partitions()[0].leader(), 0);
    assert_eq!(topic_metadata.partitions()[1].leader(), 0);
    assert_eq!(topic_metadata.partitions()[2].leader(), 0);
    assert_eq!(topic_metadata.partitions()[0].replicas(), &[0]);
    assert_eq!(topic_metadata.partitions()[0].isr(), &[0]);

    let metadata_one_topic = consumer.fetch_metadata(Some(&topic_name), 5000).unwrap();
    assert_eq!(metadata_one_topic.topics().len(), 1);
}

#[test]
fn test_subscription() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    populate_topic(&topic_name, 10, &value_fn, &key_fn, None, None);
    let consumer = create_stream_consumer(&rand_test_group(), None);
    consumer.subscribe(&[topic_name.as_str()]).unwrap();

    let _consumer_future = consumer.start().take(10).wait();

    let mut tpl = TopicPartitionList::new();
    tpl.add_topic_unassigned(&topic_name);
    assert_eq!(tpl, consumer.subscription().unwrap());
}

#[test]
fn test_group_membership() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    let group_name = rand_test_group();
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(0), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(1), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(2), None);
    let consumer = create_stream_consumer(&group_name, None);
    consumer.subscribe(&[topic_name.as_str()]).unwrap();

    // Make sure the consumer joins the group
    let _consumer_future = consumer.start()
        .take(1)
        .for_each(|_| Ok(()))
        .wait();

    let group_list = consumer.fetch_group_list(None, 5000).unwrap();

    // Print all the data, valgrind will check memory access
    for group in group_list.groups().iter() {
        println!("{} {} {} {}", group.name(), group.state(), group.protocol(), group.protocol_type());
        for member in group.members() {
            println!("  {} {} {}", member.id(), member.client_id(), member.client_host());
        }
    }

    let group_list2 = consumer.fetch_group_list(Some(&group_name), 5000).unwrap();
    assert_eq!(group_list2.groups().len(), 1);

    let consumer_group = group_list2.groups().iter().find(|&g| g.name() == group_name).unwrap();
    assert_eq!(consumer_group.members().len(), 1);

    let consumer_member = &consumer_group.members()[0];
    assert_eq!(consumer_member.client_id(), "rdkafka_integration_test_client");
}
<commit_msg>Fix test<commit_after>//! Test metadata fetch, group membership, consumer metadata.
extern crate env_logger;
extern crate futures;
extern crate rand;
extern crate rdkafka;

use futures::*;

use rdkafka::consumer::Consumer;
use rdkafka::topic_partition_list::TopicPartitionList;

mod utils;
use utils::*;

#[test]
fn test_metadata() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(0), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(1), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(2), None);
    let consumer = create_stream_consumer(&rand_test_group(), None);

    let metadata = consumer.fetch_metadata(None, 5000).unwrap();
    assert_eq!(metadata.orig_broker_id(), -1);
    assert!(metadata.orig_broker_name().len() > 0);

    let broker_metadata = metadata.brokers();
    assert_eq!(broker_metadata.len(), 1);
    assert_eq!(broker_metadata[0].id(), 0);
    assert!(broker_metadata[0].host().len() > 0);
    assert_eq!(broker_metadata[0].port(), 9092);

    let topic_metadata = metadata.topics().iter()
        .find(|m| m.name() == topic_name).unwrap();

    let mut ids = topic_metadata.partitions().iter()
        .map(|p| {
            assert_eq!(p.error(), None);
            p.id()
        })
        .collect::<Vec<_>>();
    ids.sort();

    assert_eq!(ids, vec![0, 1, 2]);
    assert_eq!(topic_metadata.error(), None);
    assert_eq!(topic_metadata.partitions().len(), 3);
    assert_eq!(topic_metadata.partitions()[0].leader(), 0);
    assert_eq!(topic_metadata.partitions()[1].leader(), 0);
    assert_eq!(topic_metadata.partitions()[2].leader(), 0);
    assert_eq!(topic_metadata.partitions()[0].replicas(), &[0]);
    assert_eq!(topic_metadata.partitions()[0].isr(), &[0]);

    let metadata_one_topic = consumer.fetch_metadata(Some(&topic_name), 5000).unwrap();
    assert_eq!(metadata_one_topic.topics().len(), 1);
}

#[test]
fn test_subscription() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    populate_topic(&topic_name, 10, &value_fn, &key_fn, None, None);
    let consumer = create_stream_consumer(&rand_test_group(), None);
    consumer.subscribe(&[topic_name.as_str()]).unwrap();

    let _consumer_future = consumer.start().take(10).wait();

    let mut tpl = TopicPartitionList::new();
    tpl.add_topic_unassigned(&topic_name);
    assert_eq!(tpl, consumer.subscription().unwrap());
}

#[test]
fn test_group_membership() {
    let _r = env_logger::init();

    let topic_name = rand_test_topic();
    let group_name = rand_test_group();
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(0), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(1), None);
    populate_topic(&topic_name, 1, &value_fn, &key_fn, Some(2), None);
    let consumer = create_stream_consumer(&group_name, None);
    consumer.subscribe(&[topic_name.as_str()]).unwrap();

    // Make sure the consumer joins the group
    let _consumer_future = consumer.start()
        .take(1)
        .for_each(|_| Ok(()))
        .wait();

    let group_list = consumer.fetch_group_list(None, 5000).unwrap();

    // Print all the data, valgrind will check memory access
    for group in group_list.groups().iter() {
        println!("{} {} {} {}", group.name(), group.state(), group.protocol(), group.protocol_type());
        for member in group.members() {
            println!("  {} {} {}", member.id(), member.client_id(), member.client_host());
        }
    }

    let group_list2 = consumer.fetch_group_list(Some(&group_name), 5000).unwrap();
    assert_eq!(group_list2.groups().len(), 1);

    let consumer_group = group_list2.groups().iter().find(|&g| g.name() == group_name).unwrap();
    assert_eq!(consumer_group.members().len(), 1);

    let consumer_member = &consumer_group.members()[0];
    assert_eq!(consumer_member.client_id(), "rdkafka_integration_test_client");
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::result::Result;
use std::boxed::Box;
use std::error::Error;
use std::rc::Rc;
use std::io::{Read, Write};

use mio::{self, Token, EventLoop, EventSet, PollOpt, TryWrite, TryRead};
use mio::tcp::{TcpListener, TcpStream};
use protobuf;
use bytes::{MutBuf, ByteBuf, MutByteBuf};

use raft::raftpb::Message;
use util::codec::{self, encode_msg, decode_msg};

const SERVER_TOKEN: Token = Token(0);

pub trait Dispatcher {
    fn Dispatch<M: protobuf::Message>(&self, m: M) -> Result<M, Box<Error + Send + Sync>>;
}

struct Client<D: Dispatcher> {
    sock: TcpStream,
    interest: EventSet,
    d: Rc<D>,

    res: MutByteBuf,
}

impl<D: Dispatcher> Client<D> {
    fn new(sock: TcpStream, d: Rc<D>) -> Client<D> {
        Client {
            sock: sock,
            interest: EventSet::readable(),
            d: d,
            res: ByteBuf::mut_with_capacity(0),
        }
    }

    fn write(&mut self) {
        self.sock.write(self.res.bytes()).unwrap();
        self.interest.remove(EventSet::writable());
        self.interest.insert(EventSet::readable());
    }

    fn read(&mut self) {
        // only test here
        let mut m = Message::new();
        let msg_id = decode_msg(&mut self.sock, &mut m).unwrap();

        let res = self.d.Dispatch(m).unwrap();
        self.res.clear();
        encode_msg(&mut self.res, msg_id, &res).unwrap();

        self.interest.remove(EventSet::readable());
        self.interest.insert(EventSet::writable());
    }
}

struct Server<D: Dispatcher> {
    listener: TcpListener,
    clients: HashMap<Token, Client<D>>,
    token_counter: usize,
    d: Rc<D>,
}

impl<D: Dispatcher> mio::Handler for Server<D> {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Server<D>>, token: Token, events: EventSet) {
        if events.is_hup() || events.is_error() {
            self.clients.remove(&token);
            return;
        }

        if events.is_readable() {
            match token {
                SERVER_TOKEN => {
                    let sock = match self.listener.accept() {
                        Ok(Some((sock, addr))) => sock,
                        Ok(None) => unreachable!(),
                        Err(e) => {
                            error!("accept error: {}", e);
                            return;
                        }
                    };

                    let new_token = Token(self.token_counter);
                    let d = self.d.clone();
                    self.clients.insert(new_token, Client::new(sock, d));
                    self.token_counter += 1;

                    event_loop.register(&self.clients[&new_token].sock,
                                        new_token,
                                        EventSet::readable(),
                                        PollOpt::edge() | PollOpt::oneshot())
                              .unwrap();

                }
                token => {
                    let mut client = self.clients.get_mut(&token).unwrap();
                    client.read();
                    event_loop.reregister(&client.sock,
                                          token,
                                          client.interest,
                                          PollOpt::edge() | PollOpt::oneshot())
                              .unwrap();
                }
            }
        }

        if events.is_writable() {
            let mut client = self.clients.get_mut(&token).unwrap();
            client.write();
            event_loop.reregister(&client.sock,
                                  token,
                                  client.interest,
                                  PollOpt::edge() | PollOpt::oneshot())
                      .unwrap();
        }
    }
}

fn run<D: Dispatcher>(addr: &str, d: D) {
    let laddr = addr.parse().unwrap();
    let listener = TcpListener::bind(&laddr).unwrap();

    let mut event_loop = EventLoop::new().unwrap();

    event_loop.register(&listener,
                        SERVER_TOKEN,
                        EventSet::readable(),
                        PollOpt::edge())
              .unwrap();

    let mut server = Server {
        listener: listener,
        token_counter: 1,
        clients: HashMap::new(),
        d: Rc::new(d),
    };

    event_loop.run(&mut server).unwrap();
}
<commit_msg>kvserver: support mutable<commit_after>use std::collections::HashMap;
use std::result::Result;
use std::boxed::Box;
use std::error::Error;
use std::rc::Rc;
use std::cell::RefCell;
use std::io::{Read, Write};

use mio::{self, Token, EventLoop, EventSet, PollOpt, TryWrite, TryRead};
use mio::tcp::{TcpListener, TcpStream};
use protobuf;
use bytes::{MutBuf, ByteBuf, MutByteBuf};

use raft::raftpb::Message;
use util::codec::{self, encode_msg, decode_msg};

const SERVER_TOKEN: Token = Token(0);

pub trait Dispatcher {
    fn Dispatch<M: protobuf::Message>(&mut self, m: M) -> Result<M, Box<Error + Send + Sync>>;
}

struct Client<D: Dispatcher> {
    sock: TcpStream,
    interest: EventSet,
    d: Rc<RefCell<D>>,

    res: MutByteBuf,
}

impl<D: Dispatcher> Client<D> {
    fn new(sock: TcpStream, d: Rc<RefCell<D>>) -> Client<D> {
        Client {
            sock: sock,
            interest: EventSet::readable(),
            d: d,
            res: ByteBuf::mut_with_capacity(0),
        }
    }

    fn write(&mut self) {
        self.sock.write(self.res.bytes()).unwrap();
        self.interest.remove(EventSet::writable());
        self.interest.insert(EventSet::readable());
    }

    fn read(&mut self) {
        // only test here
        let mut m = Message::new();
        let msg_id = decode_msg(&mut self.sock, &mut m).unwrap();

        let res = self.d.borrow_mut().Dispatch(m).unwrap();
        self.res.clear();
        encode_msg(&mut self.res, msg_id, &res).unwrap();

        self.interest.remove(EventSet::readable());
        self.interest.insert(EventSet::writable());
    }
}

struct Server<D: Dispatcher> {
    listener: TcpListener,
    clients: HashMap<Token, Client<D>>,
    token_counter: usize,
    d: Rc<RefCell<D>>,
}

impl<D: Dispatcher> mio::Handler for Server<D> {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Server<D>>, token: Token, events: EventSet) {
        if events.is_hup() || events.is_error() {
            self.clients.remove(&token);
            return;
        }

        if events.is_readable() {
            match token {
                SERVER_TOKEN => {
                    let sock = match self.listener.accept() {
                        Ok(Some((sock, addr))) => sock,
                        Ok(None) => unreachable!(),
                        Err(e) => {
                            error!("accept error: {}", e);
                            return;
                        }
                    };

                    let new_token = Token(self.token_counter);
                    let d = self.d.clone();
                    self.clients.insert(new_token, Client::new(sock, d));
                    self.token_counter += 1;

                    event_loop.register(&self.clients[&new_token].sock,
                                        new_token,
                                        EventSet::readable(),
                                        PollOpt::edge() | PollOpt::oneshot())
                              .unwrap();

                }
                token => {
                    let mut client = self.clients.get_mut(&token).unwrap();
                    client.read();
                    event_loop.reregister(&client.sock,
                                          token,
                                          client.interest,
                                          PollOpt::edge() | PollOpt::oneshot())
                              .unwrap();
                }
            }
        }

        if events.is_writable() {
            let mut client = self.clients.get_mut(&token).unwrap();
            client.write();
            event_loop.reregister(&client.sock,
                                  token,
                                  client.interest,
                                  PollOpt::edge() | PollOpt::oneshot())
                      .unwrap();
        }
    }
}

fn run<D: Dispatcher>(addr: &str, d: D) {
    let laddr = addr.parse().unwrap();
    let listener = TcpListener::bind(&laddr).unwrap();

    let mut event_loop = EventLoop::new().unwrap();

    event_loop.register(&listener,
                        SERVER_TOKEN,
                        EventSet::readable(),
                        PollOpt::edge())
              .unwrap();

    let mut server = Server {
        listener: listener,
        token_counter: 1,
        clients: HashMap::new(),
        d: Rc::new(RefCell::new(d)),
    };

    event_loop.run(&mut server).unwrap();
}
<|endoftext|>


<commit_before>use std::env;
use std::io::Read;
use std::fs::File;
use std::path::PathBuf;
use std::collections::HashMap;

fn main() {
    let mut data_path: std::path::PathBuf = env::current_dir().unwrap();
    data_path.push("data");
    data_path.push("input.txt");

    let directions: Vec<String> = data_from_file(&data_path).unwrap();

    let longest_distance: i32 = longest_block_distance(&directions);

    println!("Answer 1: {}", longest_distance);
}

fn data_from_file(file_name: &PathBuf) -> Result<Vec<String>, std::io::Error> {
    let mut data_file: File = File::open(file_name)?;
    let mut data_line: String = String::new();

    data_file.read_to_string(&mut data_line)?;

    let data: Vec<String> = data_line.trim()
                                     .split(", ")
                                     .map(|str_literal| str_literal.to_owned())
                                     .collect();

    Ok(data)
}

fn longest_block_distance(directions: &Vec<String>) -> i32 {
    let mut index: i32 = 0;
    let mut ew_parity: i32 = 1;
    let mut ns_parity: i32 = 1;

    let mut direction_parity_table: HashMap<char, i32> = HashMap::new();
    direction_parity_table.insert('R', 1);
    direction_parity_table.insert('L', -1);

    let mut travel_location: [i32; 2] = [0, 0];

    for direction in directions {
        let direction_parity: &i32 = direction_parity_table.get(
            &(direction.chars().nth(0).unwrap())).unwrap();
        let distance: i32 = (&direction[1..]).parse().unwrap();

        if index % 2 == 0 {
            if ns_parity == 1 {
                ew_parity = *direction_parity;
            } else {
                ew_parity = -1 * (*direction_parity);
            }

            travel_location[1] += ew_parity * distance;
        } else {
            if ew_parity == 1 {
                ns_parity = -1 * (*direction_parity);
            } else {
                ns_parity = *direction_parity;
            }

            travel_location[0] += ns_parity * distance;
       }

        index += 1;
    }

    return travel_location.iter().sum();
}<commit_msg>Starting Day, part 2.<commit_after>use std::env;
use std::io::Read;
use std::fs::File;
use std::path::PathBuf;
use std::collections::HashMap;

fn main() {
    let mut data_path: std::path::PathBuf = env::current_dir().unwrap();
    data_path.push("data");
    data_path.push("input.txt");

    let directions: Vec<String> = data_from_file(&data_path).unwrap();

    let longest_distance: i32 = longest_block_distance(&directions);

    println!("Answer 1: {}", longest_distance);
}

pub fn data_from_file(file_name: &PathBuf) ->
        Result<Vec<String>, std::io::Error> {
    let mut data_file: File = File::open(file_name)?;
    let mut data_line: String = String::new();

    data_file.read_to_string(&mut data_line)?;

    let data: Vec<String> = data_line.trim()
                                     .split(", ")
                                     .map(|str_literal| str_literal.to_owned())
                                     .collect();

    Ok(data)
}

pub fn longest_block_distance(directions: &Vec<String>) -> i32 {
    let mut index: i32 = 0;
    let mut ew_parity: i32 = 1;
    let mut ns_parity: i32 = 1;

    let mut direction_parity_table: HashMap<char, i32> = HashMap::new();
    direction_parity_table.insert('R', 1);
    direction_parity_table.insert('L', -1);

    let mut travel_location: [i32; 2] = [0, 0];

    for direction in directions {
        let direction_parity: &i32 = direction_parity_table.get(
            &(direction.chars().nth(0).unwrap())).unwrap();
        let distance: i32 = (&direction[1..]).parse().unwrap();

        if index % 2 == 0 {
            if ns_parity == 1 {
                ew_parity = *direction_parity;
            } else {
                ew_parity = -1 * (*direction_parity);
            }

            travel_location[1] += ew_parity * distance;
        } else {
            if ew_parity == 1 {
                ns_parity = -1 * (*direction_parity);
            } else {
                ns_parity = *direction_parity;
            }

            travel_location[0] += ns_parity * distance;
       }

        index += 1;
    }

    return travel_location.iter().sum();
}

// Testing
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_longest_distance() {
        assert_eq!(longest_block_distance(&vec!["R2", "L3"]), 5);
    }
}<|endoftext|>


<commit_before>use std::sync::{Arc, Mutex};

use futures::{future, sync::mpsc, Async, Future, Poll, Stream};
#[cfg(feature = "dnssec")]
use proto::SecureDnsHandle;
use proto::{
    error::ProtoResult,
    rr::{Name, RData, RecordType},
    xfer::{DnsRequestOptions, RetryDnsHandle},
};

use config::{ResolverConfig, ResolverOpts};
use dns_lru::DnsLru;
use hosts::Hosts;
use lookup::{Lookup, LookupEither, LookupFuture};
use lookup_ip::LookupIpFuture;
use lookup_state::CachingClient;
use name_server_pool::{ConnectionHandle, NameServerPool, StandardConnection};
use proto::op::Query;

use super::Request;

/// Returns a new future that will drive the background task.
///
/// This background task manages the [`NameServerPool`] and other state used
/// to drive lookups. When this future is spawned on an executor, it will
/// first construct the [`NameServerPool`] and configure the client state,
/// before yielding. When polled again, it will check for any incoming lookup
/// requests, handle them, and then yield again, as long as there are still any
/// [`AsyncResolver`] handles linked to that background task. When all of its
/// [`AsyncResolver`]s have been dropped, the background future will finish.
pub(super) fn task(
    config: ResolverConfig,
    options: ResolverOpts,
    lru: Arc<Mutex<DnsLru>>,
    request_rx: mpsc::UnboundedReceiver<Request>,
) -> impl Future<Item = (), Error = ()> {
    future::lazy(move || {
        let pool =
            NameServerPool::<ConnectionHandle, StandardConnection>::from_config(&config, &options);
        let either;
        let client = RetryDnsHandle::new(pool.clone(), options.attempts);
        if options.validate {
            #[cfg(feature = "dnssec")]
            {
                either = LookupEither::Secure(SecureDnsHandle::new(client));
            }

            #[cfg(not(feature = "dnssec"))]
            {
                // TODO: should this just be a panic, or a pinned error?
                warn!("validate option is only available with 'dnssec' feature");
                either = LookupEither::Retry(client);
            }
        } else {
            either = LookupEither::Retry(client);
        }

        let hosts = if options.use_hosts_file {
            Some(Arc::new(Hosts::new()))
        } else {
            None
        };

        Task {
            config,
            options,
            client_cache: CachingClient::with_cache(lru, either),
            hosts: hosts,
            request_rx,
        }
    })
}

type ClientCache = CachingClient<LookupEither<ConnectionHandle, StandardConnection>>;

/// Background task that resolves DNS queries.
struct Task {
    config: ResolverConfig,
    options: ResolverOpts,
    client_cache: ClientCache,
    hosts: Option<Arc<Hosts>>,
    request_rx: mpsc::UnboundedReceiver<Request>,
}

impl Task {
    fn lookup(
        &self,
        name: Name,
        record_type: RecordType,
        options: DnsRequestOptions,
    ) -> LookupFuture {
        let names = self.build_names(name);
        LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
    }

    fn lookup_ip(&self, maybe_name: ProtoResult<Name>, maybe_ip: Option<RData>) -> LookupIpFuture {
        let mut finally_ip_addr = None;

        // if host is a ip address, return directly.
        if let Some(ip_addr) = maybe_ip {
            // if ndots are greater than 4, then we can't assume the name is an IpAddr
            //   this accepts IPv6 as well, b/c IPv6 can take the form: 2001:db8::198.51.100.35
            //   but `:` is not a valid DNS character, so techinically this will fail parsing.
            //   TODO: should we always do search before returning this?
            if self.options.ndots > 4 {
                finally_ip_addr = Some(ip_addr);
            } else {
                return LookupIpFuture::ok(
                    self.client_cache.clone(),
                    Lookup::new_with_max_ttl(Query::new(), Arc::new(vec![ip_addr])),
                );
            }
        }

        let name = match (maybe_name, finally_ip_addr.as_ref()) {
            (Ok(name), _) => name,
            (Err(_), Some(ip_addr)) => {
                // it was a valid IP, return that...
                return LookupIpFuture::ok(
                    self.client_cache.clone(),
                    Lookup::new_with_max_ttl(Query::new(), Arc::new(vec![ip_addr.clone()])),
                );
            }
            (Err(err), None) => {
                return LookupIpFuture::error(self.client_cache.clone(), err);
            }
        };

        let names = self.build_names(name);
        let hosts = self.hosts.as_ref().cloned();

        LookupIpFuture::lookup(
            names,
            self.options.ip_strategy,
            self.client_cache.clone(),
            DnsRequestOptions::default(),
            hosts,
            finally_ip_addr,
        )
    }

    fn push_name(name: Name, names: &mut Vec<Name>) {
        if !names.contains(&name) {
            names.push(name);
        }
    }

    fn build_names(&self, name: Name) -> Vec<Name> {
        // if it's fully qualified, we can short circuit the lookup logic
        if name.is_fqdn() {
            vec![name]
        } else {
            // Otherwise we have to build the search list
            // Note: the vec is built in reverse order of precedence, for stack semantics
            let mut names =
                Vec::<Name>::with_capacity(1 /*FQDN*/ + 1 /*DOMAIN*/ + self.config.search().len());

            // if not meeting ndots, we always do the raw name in the final lookup, or it's a localhost...
            let raw_name_first: bool =
                name.num_labels() as usize > self.options.ndots || name.is_localhost();

            // if not meeting ndots, we always do the raw name in the final lookup
            if !raw_name_first {
                names.push(name.clone());
            }

            for search in self.config.search().iter().rev() {
                let name_search = name.clone().append_domain(search);
                Self::push_name(name_search, &mut names);
            }

            if let Some(domain) = self.config.domain() {
                let name_search = name.clone().append_domain(domain);
                Self::push_name(name_search, &mut names);
            }

            // this is the direct name lookup
            if raw_name_first {
                // adding the name as though it's an FQDN for lookup
                names.push(name.clone());
            }

            names
        }
    }
}

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

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let poll = self.request_rx.poll().map_err(|e| {
                error!("AsyncResolver poisoned: {:?}", e);
            });
            match try_ready!(poll) {
                None => {
                    // mpsc::UnboundedReceiver::poll() returns `None` when the sender
                    // has been dropped.
                    trace!("AsyncResolver dropped, shutting down background task.");
                    // Return `Ready` so the background future finishes, as no handles
                    // are using it any longer.
                    return Ok(Async::Ready(()));
                }
                Some(Request::Lookup {
                    name,
                    record_type,
                    options,
                    tx,
                }) => {
                    let future = self.lookup(name, record_type, options);
                    // tx.send() will return an error if the oneshot was canceled, but
                    // we don't actually care, so just drop the future.
                    let _ = tx.send(future);
                }
                Some(Request::Ip {
                    maybe_name,
                    maybe_ip,
                    tx,
                }) => {
                    let future = self.lookup_ip(maybe_name, maybe_ip);
                    // tx.send() will return an error if the oneshot was canceled, but
                    // we don't actually care, so just drop the future.
                    let _ = tx.send(future);
                }
            }
        }
    }
}
<commit_msg>Synthesize reasonable queries when a query is not specified<commit_after>use std::sync::{Arc, Mutex};

use futures::{future, sync::mpsc, Async, Future, Poll, Stream};
#[cfg(feature = "dnssec")]
use proto::SecureDnsHandle;
use proto::{
    error::ProtoResult,
    rr::{Name, RData, RecordType},
    xfer::{DnsRequestOptions, RetryDnsHandle},
};

use config::{ResolverConfig, ResolverOpts};
use dns_lru::DnsLru;
use hosts::Hosts;
use lookup::{Lookup, LookupEither, LookupFuture};
use lookup_ip::LookupIpFuture;
use lookup_state::CachingClient;
use name_server_pool::{ConnectionHandle, NameServerPool, StandardConnection};
use proto::op::Query;

use super::Request;

/// Returns a new future that will drive the background task.
///
/// This background task manages the [`NameServerPool`] and other state used
/// to drive lookups. When this future is spawned on an executor, it will
/// first construct the [`NameServerPool`] and configure the client state,
/// before yielding. When polled again, it will check for any incoming lookup
/// requests, handle them, and then yield again, as long as there are still any
/// [`AsyncResolver`] handles linked to that background task. When all of its
/// [`AsyncResolver`]s have been dropped, the background future will finish.
pub(super) fn task(
    config: ResolverConfig,
    options: ResolverOpts,
    lru: Arc<Mutex<DnsLru>>,
    request_rx: mpsc::UnboundedReceiver<Request>,
) -> impl Future<Item = (), Error = ()> {
    future::lazy(move || {
        let pool =
            NameServerPool::<ConnectionHandle, StandardConnection>::from_config(&config, &options);
        let either;
        let client = RetryDnsHandle::new(pool.clone(), options.attempts);
        if options.validate {
            #[cfg(feature = "dnssec")]
            {
                either = LookupEither::Secure(SecureDnsHandle::new(client));
            }

            #[cfg(not(feature = "dnssec"))]
            {
                // TODO: should this just be a panic, or a pinned error?
                warn!("validate option is only available with 'dnssec' feature");
                either = LookupEither::Retry(client);
            }
        } else {
            either = LookupEither::Retry(client);
        }

        let hosts = if options.use_hosts_file {
            Some(Arc::new(Hosts::new()))
        } else {
            None
        };

        Task {
            config,
            options,
            client_cache: CachingClient::with_cache(lru, either),
            hosts: hosts,
            request_rx,
        }
    })
}

type ClientCache = CachingClient<LookupEither<ConnectionHandle, StandardConnection>>;

/// Background task that resolves DNS queries.
struct Task {
    config: ResolverConfig,
    options: ResolverOpts,
    client_cache: ClientCache,
    hosts: Option<Arc<Hosts>>,
    request_rx: mpsc::UnboundedReceiver<Request>,
}

impl Task {
    fn lookup(
        &self,
        name: Name,
        record_type: RecordType,
        options: DnsRequestOptions,
    ) -> LookupFuture {
        let names = self.build_names(name);
        LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
    }

    fn lookup_ip(&self, maybe_name: ProtoResult<Name>, maybe_ip: Option<RData>) -> LookupIpFuture {
        let mut finally_ip_addr = None;

        // if host is a ip address, return directly.
        if let Some(ip_addr) = maybe_ip {
            // if ndots are greater than 4, then we can't assume the name is an IpAddr
            //   this accepts IPv6 as well, b/c IPv6 can take the form: 2001:db8::198.51.100.35
            //   but `:` is not a valid DNS character, so techinically this will fail parsing.
            //   TODO: should we always do search before returning this?
            if self.options.ndots > 4 {
                finally_ip_addr = Some(ip_addr);
            } else {
                let query = Query::query(maybe_name.unwrap_or_default(), ip_addr.to_record_type());
                let lookup = Lookup::new_with_max_ttl(query, Arc::new(vec![ip_addr]));
                return LookupIpFuture::ok(self.client_cache.clone(), lookup);
            }
        }

        let name = match (maybe_name, finally_ip_addr.as_ref()) {
            (Ok(name), _) => name,
            (Err(_), Some(ip_addr)) => {
                // it was a valid IP, return that...
                let query = Query::query(Name::default(), ip_addr.to_record_type());
                let lookup = Lookup::new_with_max_ttl(query, Arc::new(vec![ip_addr.clone()]));
                return LookupIpFuture::ok(self.client_cache.clone(), lookup);
            }
            (Err(err), None) => {
                return LookupIpFuture::error(self.client_cache.clone(), err);
            }
        };

        let names = self.build_names(name);
        let hosts = self.hosts.as_ref().cloned();

        LookupIpFuture::lookup(
            names,
            self.options.ip_strategy,
            self.client_cache.clone(),
            DnsRequestOptions::default(),
            hosts,
            finally_ip_addr,
        )
    }

    fn push_name(name: Name, names: &mut Vec<Name>) {
        if !names.contains(&name) {
            names.push(name);
        }
    }

    fn build_names(&self, name: Name) -> Vec<Name> {
        // if it's fully qualified, we can short circuit the lookup logic
        if name.is_fqdn() {
            vec![name]
        } else {
            // Otherwise we have to build the search list
            // Note: the vec is built in reverse order of precedence, for stack semantics
            let mut names =
                Vec::<Name>::with_capacity(1 /*FQDN*/ + 1 /*DOMAIN*/ + self.config.search().len());

            // if not meeting ndots, we always do the raw name in the final lookup, or it's a localhost...
            let raw_name_first: bool =
                name.num_labels() as usize > self.options.ndots || name.is_localhost();

            // if not meeting ndots, we always do the raw name in the final lookup
            if !raw_name_first {
                names.push(name.clone());
            }

            for search in self.config.search().iter().rev() {
                let name_search = name.clone().append_domain(search);
                Self::push_name(name_search, &mut names);
            }

            if let Some(domain) = self.config.domain() {
                let name_search = name.clone().append_domain(domain);
                Self::push_name(name_search, &mut names);
            }

            // this is the direct name lookup
            if raw_name_first {
                // adding the name as though it's an FQDN for lookup
                names.push(name.clone());
            }

            names
        }
    }
}

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

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let poll = self.request_rx.poll().map_err(|e| {
                error!("AsyncResolver poisoned: {:?}", e);
            });
            match try_ready!(poll) {
                None => {
                    // mpsc::UnboundedReceiver::poll() returns `None` when the sender
                    // has been dropped.
                    trace!("AsyncResolver dropped, shutting down background task.");
                    // Return `Ready` so the background future finishes, as no handles
                    // are using it any longer.
                    return Ok(Async::Ready(()));
                }
                Some(Request::Lookup {
                    name,
                    record_type,
                    options,
                    tx,
                }) => {
                    let future = self.lookup(name, record_type, options);
                    // tx.send() will return an error if the oneshot was canceled, but
                    // we don't actually care, so just drop the future.
                    let _ = tx.send(future);
                }
                Some(Request::Ip {
                    maybe_name,
                    maybe_ip,
                    tx,
                }) => {
                    let future = self.lookup_ip(maybe_name, maybe_ip);
                    // tx.send() will return an error if the oneshot was canceled, but
                    // we don't actually care, so just drop the future.
                    let _ = tx.send(future);
                }
            }
        }
    }
}
<|endoftext|>