<commit_before>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

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

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        use hook::error::HookError as HE;
        use hook::error::HookErrorKind as HEK;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::NonMutableAccess(accessor) => try!(accessor.access(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

<commit_msg>Make hook execution parallel for NonMutableHookDataAccessor impl for Aspect<commit_after>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

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

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        use hook::error::HookError as HE;
        use hook::error::HookErrorKind as HEK;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        use crossbeam;
        use std::thread;
        use std::thread::JoinHandle;

        use hook::error::HookError as HE;
        use hook::error::HookErrorKind as HEK;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::NonMutableAccess(accessor) => accessor.access(fle),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

<|endoftext|>


<commit_before>// use std::collections::HashMap;
use std::ffi::CString;
use libc::c_int;

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow};
use types::ToSql;

impl SqliteConnection {
    /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///     conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
    }

    /// Convenience method to execute a query with named parameter(s) that is expected to return
    /// a single row.
    ///
    /// If the query returns more than one row, all rows except the first are ignored.
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn query_named_row<T, F>(&self,
                                 sql: &str,
                                 params: &[(&str, &ToSql)],
                                 f: F)
                                 -> SqliteResult<T>
        where F: FnOnce(SqliteRow) -> T
    {
        let mut stmt = try!(self.prepare(sql));
        let mut rows = try!(stmt.query_named(params));

        match rows.next() {
            Some(row) => row.map(f),
            None => {
                Err(SqliteError {
                    code: ffi::SQLITE_NOTICE,
                    message: "Query did not return a row".to_string(),
                })
            }
        }
    }
}

impl<'conn> SqliteStatement<'conn> {
    // pub fn parameter_names(&self) -> HashMap<String, i32> {
    // let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
    // let mut index_by_name = HashMap::with_capacity(n as usize);
    // for i in 1..n+1 {
    // let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) };
    // if !c_name.is_null() {
    // let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() };
    // index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n);
    // }
    // }
    // index_by_name
    // }

    /// Return the index of an SQL parameter given its name.
    ///
    /// # Failure
    ///
    /// Return None if `name` is invalid or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name| {
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n),
                }
            })

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///     let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
    ///     stmt.execute_named(&[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails, the executed statement returns rows (in
    /// which case `query` should be used instead), or the underling SQLite call fails.
    pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            try!(self.bind_named_parameters(params));
            self.execute_()
        }
    }

    /// Execute the prepared statement with named parameter(s), returning an iterator over the
    /// resulting rows.
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows};
    /// fn query(conn: &SqliteConnection) -> SqliteResult<()> {
    ///     let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
    ///     let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
    ///     for row in rows {
    ///         // ...
    ///     }
    ///     Ok(())
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails.
    pub fn query_named<'a>(&'a mut self,
                           params: &[(&str, &ToSql)])
                           -> SqliteResult<SqliteRows<'a>> {
        self.reset_if_needed();

        unsafe {
            try!(self.bind_named_parameters(params));
        }

        self.needs_reset = true;
        Ok(SqliteRows::new(self))
    }

    unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> {
        // Always check that the number of parameters is correct.
        assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt),
                "incorrect number of parameters to query(): expected {}, got {}",
                ffi::sqlite3_bind_parameter_count(self.stmt),
                params.len());

        // In debug, also sanity check that we got distinct parameter names.
        debug_assert!({
                          use std::collections::HashSet;

                          let mut s = HashSet::with_capacity(params.len());
                          for &(name, _) in params {
                              s.insert(name);
                          }

                          s.len() == params.len()
                      },
                      "named parameters must be unique");

        for &(name, value) in params {
            let i = try!(self.parameter_index(name).ok_or(SqliteError {
                code: ffi::SQLITE_MISUSE,
                message: format!("Invalid parameter name: {}", name),
            }));
            try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

    #[test]
    fn test_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();

        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
                   1);
        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
                   1);

        assert_eq!(3i32,
                   db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x",
                                      &[(":x", &0i32)],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_stmt_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
        stmt.execute_named(&[(":name", &"one")]).unwrap();

        assert_eq!(1i32,
                   db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name",
                                      &[(":name", &"one")],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_query_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
        stmt.query_named(&[(":name", &"one")]).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_panic_on_incorrect_number_of_parameters() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32)]);
    }

    #[test]
    #[cfg(debug_assertions)]
    #[should_panic]
    fn test_debug_panic_on_incorrect_parameter_names() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32), (":one", &2i32)]);
    }
}
<commit_msg>Remove commented-out code<commit_after>use std::ffi::CString;
use libc::c_int;

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow};
use types::ToSql;

impl SqliteConnection {
    /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///     conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
    }

    /// Convenience method to execute a query with named parameter(s) that is expected to return
    /// a single row.
    ///
    /// If the query returns more than one row, all rows except the first are ignored.
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn query_named_row<T, F>(&self,
                                 sql: &str,
                                 params: &[(&str, &ToSql)],
                                 f: F)
                                 -> SqliteResult<T>
        where F: FnOnce(SqliteRow) -> T
    {
        let mut stmt = try!(self.prepare(sql));
        let mut rows = try!(stmt.query_named(params));

        match rows.next() {
            Some(row) => row.map(f),
            None => {
                Err(SqliteError {
                    code: ffi::SQLITE_NOTICE,
                    message: "Query did not return a row".to_string(),
                })
            }
        }
    }
}

impl<'conn> SqliteStatement<'conn> {
    /// Return the index of an SQL parameter given its name.
    ///
    /// # Failure
    ///
    /// Return None if `name` is invalid or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name| {
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n),
                }
            })

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///     let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
    ///     stmt.execute_named(&[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails, the executed statement returns rows (in
    /// which case `query` should be used instead), or the underling SQLite call fails.
    pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            try!(self.bind_named_parameters(params));
            self.execute_()
        }
    }

    /// Execute the prepared statement with named parameter(s), returning an iterator over the
    /// resulting rows.
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows};
    /// fn query(conn: &SqliteConnection) -> SqliteResult<()> {
    ///     let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
    ///     let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
    ///     for row in rows {
    ///         // ...
    ///     }
    ///     Ok(())
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails.
    pub fn query_named<'a>(&'a mut self,
                           params: &[(&str, &ToSql)])
                           -> SqliteResult<SqliteRows<'a>> {
        self.reset_if_needed();

        unsafe {
            try!(self.bind_named_parameters(params));
        }

        self.needs_reset = true;
        Ok(SqliteRows::new(self))
    }

    unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> {
        // Always check that the number of parameters is correct.
        assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt),
                "incorrect number of parameters to query(): expected {}, got {}",
                ffi::sqlite3_bind_parameter_count(self.stmt),
                params.len());

        // In debug, also sanity check that we got distinct parameter names.
        debug_assert!({
                          use std::collections::HashSet;

                          let mut s = HashSet::with_capacity(params.len());
                          for &(name, _) in params {
                              s.insert(name);
                          }

                          s.len() == params.len()
                      },
                      "named parameters must be unique");

        for &(name, value) in params {
            let i = try!(self.parameter_index(name).ok_or(SqliteError {
                code: ffi::SQLITE_MISUSE,
                message: format!("Invalid parameter name: {}", name),
            }));
            try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

    #[test]
    fn test_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();

        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
                   1);
        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
                   1);

        assert_eq!(3i32,
                   db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x",
                                      &[(":x", &0i32)],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_stmt_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
        stmt.execute_named(&[(":name", &"one")]).unwrap();

        assert_eq!(1i32,
                   db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name",
                                      &[(":name", &"one")],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_query_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
        stmt.query_named(&[(":name", &"one")]).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_panic_on_incorrect_number_of_parameters() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32)]);
    }

    #[test]
    #[cfg(debug_assertions)]
    #[should_panic]
    fn test_debug_panic_on_incorrect_parameter_names() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32), (":one", &2i32)]);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate log4rs;

#[macro_use]
extern crate nickel;
use nickel::{Request, Response, MiddlewareResult, Nickel, MediaType};
use nickel::status::StatusCode;
use nickel::router::http_router::HttpRouter;

extern crate modifier;
use modifier::Modifier;

#[macro_use]
extern crate hyper;
use hyper::header::ContentLength;

use std::io::Read;

extern crate url;
use url::form_urlencoded;

extern crate webdriver;
use webdriver::response::{WebDriverResponse, ValueResponse};

use std::time;
use std::thread;

extern crate core;

extern crate rustc_serialize;

extern crate image;
use image::GenericImage;
use std::io::Cursor;

extern crate get_if_addrs;

extern crate plugin;
extern crate typemap;

mod chromedriver;
use chromedriver::{WebdriverRequestExtensions, WebdriverSession};

use core::ops::Deref;
extern crate rand;
extern crate regex;
#[macro_use]
extern crate lazy_static;

header! { (XExtra, "X-Extra") => [String] }

impl<'a, D> Modifier<Response<'a, D>> for XExtra {
    fn modify(self, res: &mut Response<'a, D>) {
        res.headers_mut().set(self)
    }
}

fn xpath_helper(session: &WebdriverSession, xpath: String) -> Result<ValueResponse, String> {
    match session.find_element_by_xpath(xpath) {
        Err(val) => Err(format!("Error while trying to get element: {:?}", val)),
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => Ok(obj),
                _ => Err(format!("Didn't expect {:?}", val)),
            }
        }
    }
}

fn streams<'a, D>(request: &mut Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let mut buffer = String::new();
    request.origin.read_to_string(&mut buffer).unwrap();
    let mut parse = form_urlencoded::parse(buffer.as_bytes());
    let mut url = match parse.find(|k| k.0 == "url") {
        Some((_, value)) => value.into_owned(),
        None => return res.error(StatusCode::BadRequest, "No URL in request"),

    };
    let request_url = match url::Url::parse(&url) {
        Ok(value) => value,
        Err(_) => {
            return res.error(StatusCode::BadRequest,
                             format!("Request URL was dodgy: '{}'", url))
        }

    };
    let host = request_url.host_str().unwrap();
    let mut device_name: &str = "Laptop with HiDPI screen";
    let extra_fn: fn(WebdriverSession) -> String;
    let xpath = match host {
            "livestream.com" => {
                fn extra(session: WebdriverSession) -> String {
                    lazy_static! {
                        static ref RE: regex::Regex =
                            regex::Regex::new(
                                r"app-argument=http://livestream.com/accounts/(\d+)/events/(\d+)")
                        .unwrap();
                    }
                    let source = session.get_page_source();
                    let caps = RE.captures(&source).unwrap();
                    format!("[\"{}\",\"{}\"]", &caps[1], &caps[2])
                }
                extra_fn = extra;
                device_name = "Apple iPhone 5";
                "//img[@class='thumbnail_image']"
            }
            "www.ustream.tv" => {
                fn extra(session: WebdriverSession) -> String {
                    let res = xpath_helper(&session, "//video[@id='UViewer']".to_string());
                    match res {
                        Ok(val) => format!("\"{}\"", session.get_element_attribute(&val, "src")),
                        Err(val) => {
                            warn!("Error while trying to get Ustream extra data {}", val);
                            "".to_string()
                        }
                    }
                }
                extra_fn = extra;
                device_name = "Apple iPhone 5";
                "//video[@id='UViewer']"
            }
            "www.youtube.com" => {
                fn extra(_: WebdriverSession) -> String {
                    "".to_string()
                }
                lazy_static! {
                    static ref RE: regex::Regex =
                        regex::Regex::new(
                            r"https://www.youtube.com/embed/([A-Za-z0-9_]+)")
                    .unwrap();
                }
                let id = match RE.captures(&url) {
                    Some(val) => String::from(&val[1]),
                    None => {
                        return res.error(StatusCode::BadRequest,
                                         format!("Bad Youtube URL: '{}'", url))
                    }
                };

                let mut status_res = hyper::client::Client::new()
                    .get(&format!("https://www.youtube.com/get_video_info?video_id={}", id))
                    .send()
                    .unwrap();
                let mut buffer = String::new();
                status_res.read_to_string(&mut buffer).unwrap();
                lazy_static! {
                    static ref STATUS_RE: regex::Regex =
                        regex::Regex::new(
                            r"&status=([^&]+)&")
                    .unwrap();
                }
                let status_caps = STATUS_RE.captures(&buffer).unwrap();
                if &status_caps[1] == "fail" {
                    return res.error(StatusCode::BadRequest,
                                     format!("Youtube feed not running: '{}'", url));
                }
                extra_fn = extra;
                url = url + "?autoplay=1";
                "//div[@id='player']"
            }
            _ => {
                return res.error(StatusCode::BadRequest,
                                 format!("Request URL host ({}) wasn't in known list: '{}'",
                                         host,
                                         url))
            }

        }
        .to_string();
    let session = request.webdriver().deref().make_session(device_name);
    session.goto_url(url);
    thread::sleep(time::Duration::from_secs(5));
    let element: ValueResponse = match session.find_element_by_xpath(xpath) {
        Err(val) => {
            return res.error(StatusCode::BadRequest,
                             format!("Error while trying to get element: {:?}", val))
        }
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => obj,
                _ => return res.error(StatusCode::BadRequest, format!("Didn't expect {:?}", val)),
            }
        }
    };
    let element_location = {
        let loc = session.get_element_location(&element);
        loc.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let element_size = {
        let size = session.get_element_size(&element);
        size.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let screenshot = {
        let png = session.get_screenshot_as_png();
        png.unwrap()
    };

    let cursor = Cursor::new(&screenshot);
    let mut loaded_image = image::load(cursor, image::ImageFormat::PNG).unwrap();
    let (width, height) = loaded_image.dimensions();
    debug!("Loaded image dimensions: {} x {}", width, height);
    let cropped = loaded_image.crop(
		element_location.find("x").expect("x").as_f64().expect("numeric x") as u32,
		element_location.find("y").expect("y").as_f64().expect("numeric y") as u32,
		element_size.find("width").expect("width").as_f64().expect("numeric width") as u32,
		element_size.find("height").expect("height").as_f64().expect("numeric height") as u32);

    let mut output_buffer: Vec<u8> = Vec::new();
    cropped.save(&mut output_buffer, image::ImageFormat::PNG).unwrap();

    let extra = extra_fn(session);
    res.set(XExtra(extra));

    res.set(MediaType::Png);
    res.set(ContentLength(output_buffer.len() as u64));
    res.send(output_buffer)
}

fn run(ip: std::net::IpAddr, port: u16, client: chromedriver::Webdriver) {
    let mut server = Nickel::new();
    server.utilize(chromedriver::WebdriverMiddleware::new(client));
    server.post("/streams", streams);
    server.listen((ip, port));
}

fn main() {
    log4rs::init_file("log.toml", Default::default()).unwrap();
    let port = 8000;
    let mut handles = Vec::new();
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        handles.push(::std::thread::spawn(move || {
            let client = chromedriver::Webdriver::new();
            let ip = iface.ip();
            info!("Listening on {}:{} for {}", ip, port, iface.name);
            run(ip, port, client);
        }));
    }

    info!("All listeners spawned");

    for handle in handles {
        handle.join().unwrap();
    }
}
<commit_msg>Youtube ids can also have - in them<commit_after>#[macro_use]
extern crate log;
extern crate log4rs;

#[macro_use]
extern crate nickel;
use nickel::{Request, Response, MiddlewareResult, Nickel, MediaType};
use nickel::status::StatusCode;
use nickel::router::http_router::HttpRouter;

extern crate modifier;
use modifier::Modifier;

#[macro_use]
extern crate hyper;
use hyper::header::ContentLength;

use std::io::Read;

extern crate url;
use url::form_urlencoded;

extern crate webdriver;
use webdriver::response::{WebDriverResponse, ValueResponse};

use std::time;
use std::thread;

extern crate core;

extern crate rustc_serialize;

extern crate image;
use image::GenericImage;
use std::io::Cursor;

extern crate get_if_addrs;

extern crate plugin;
extern crate typemap;

mod chromedriver;
use chromedriver::{WebdriverRequestExtensions, WebdriverSession};

use core::ops::Deref;
extern crate rand;
extern crate regex;
#[macro_use]
extern crate lazy_static;

header! { (XExtra, "X-Extra") => [String] }

impl<'a, D> Modifier<Response<'a, D>> for XExtra {
    fn modify(self, res: &mut Response<'a, D>) {
        res.headers_mut().set(self)
    }
}

fn xpath_helper(session: &WebdriverSession, xpath: String) -> Result<ValueResponse, String> {
    match session.find_element_by_xpath(xpath) {
        Err(val) => Err(format!("Error while trying to get element: {:?}", val)),
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => Ok(obj),
                _ => Err(format!("Didn't expect {:?}", val)),
            }
        }
    }
}

fn streams<'a, D>(request: &mut Request<D>, mut res: Response<'a, D>) -> MiddlewareResult<'a, D> {
    let mut buffer = String::new();
    request.origin.read_to_string(&mut buffer).unwrap();
    let mut parse = form_urlencoded::parse(buffer.as_bytes());
    let mut url = match parse.find(|k| k.0 == "url") {
        Some((_, value)) => value.into_owned(),
        None => return res.error(StatusCode::BadRequest, "No URL in request"),

    };
    let request_url = match url::Url::parse(&url) {
        Ok(value) => value,
        Err(_) => {
            return res.error(StatusCode::BadRequest,
                             format!("Request URL was dodgy: '{}'", url))
        }

    };
    let host = request_url.host_str().unwrap();
    let mut device_name: &str = "Laptop with HiDPI screen";
    let extra_fn: fn(WebdriverSession) -> String;
    let xpath = match host {
            "livestream.com" => {
                fn extra(session: WebdriverSession) -> String {
                    lazy_static! {
                        static ref RE: regex::Regex =
                            regex::Regex::new(
                                r"app-argument=http://livestream.com/accounts/(\d+)/events/(\d+)")
                        .unwrap();
                    }
                    let source = session.get_page_source();
                    let caps = RE.captures(&source).unwrap();
                    format!("[\"{}\",\"{}\"]", &caps[1], &caps[2])
                }
                extra_fn = extra;
                device_name = "Apple iPhone 5";
                "//img[@class='thumbnail_image']"
            }
            "www.ustream.tv" => {
                fn extra(session: WebdriverSession) -> String {
                    let res = xpath_helper(&session, "//video[@id='UViewer']".to_string());
                    match res {
                        Ok(val) => format!("\"{}\"", session.get_element_attribute(&val, "src")),
                        Err(val) => {
                            warn!("Error while trying to get Ustream extra data {}", val);
                            "".to_string()
                        }
                    }
                }
                extra_fn = extra;
                device_name = "Apple iPhone 5";
                "//video[@id='UViewer']"
            }
            "www.youtube.com" => {
                fn extra(_: WebdriverSession) -> String {
                    "".to_string()
                }
                lazy_static! {
                    static ref RE: regex::Regex =
                        regex::Regex::new(
                            r"https://www.youtube.com/embed/([A-Za-z0-9_-]+)")
                    .unwrap();
                }
                let id = match RE.captures(&url) {
                    Some(val) => String::from(&val[1]),
                    None => {
                        return res.error(StatusCode::BadRequest,
                                         format!("Bad Youtube URL: '{}'", url))
                    }
                };

                let mut status_res = hyper::client::Client::new()
                    .get(&format!("https://www.youtube.com/get_video_info?video_id={}", id))
                    .send()
                    .unwrap();
                let mut buffer = String::new();
                status_res.read_to_string(&mut buffer).unwrap();
                lazy_static! {
                    static ref STATUS_RE: regex::Regex =
                        regex::Regex::new(
                            r"&status=([^&]+)&")
                    .unwrap();
                }
                let status_caps = STATUS_RE.captures(&buffer).unwrap();
                if &status_caps[1] == "fail" {
                    return res.error(StatusCode::BadRequest,
                                     format!("Youtube feed not running: '{}'", url));
                }
                extra_fn = extra;
                url = url + "?autoplay=1";
                "//div[@id='player']"
            }
            _ => {
                return res.error(StatusCode::BadRequest,
                                 format!("Request URL host ({}) wasn't in known list: '{}'",
                                         host,
                                         url))
            }

        }
        .to_string();
    let session = request.webdriver().deref().make_session(device_name);
    session.goto_url(url);
    thread::sleep(time::Duration::from_secs(5));
    let element: ValueResponse = match session.find_element_by_xpath(xpath) {
        Err(val) => {
            return res.error(StatusCode::BadRequest,
                             format!("Error while trying to get element: {:?}", val))
        }
        Ok(val) => {
            match val {
                WebDriverResponse::Generic(obj) => obj,
                _ => return res.error(StatusCode::BadRequest, format!("Didn't expect {:?}", val)),
            }
        }
    };
    let element_location = {
        let loc = session.get_element_location(&element);
        loc.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let element_size = {
        let size = session.get_element_size(&element);
        size.unwrap()
            .find("value")
            .expect("value")
            .clone()
    };
    let screenshot = {
        let png = session.get_screenshot_as_png();
        png.unwrap()
    };

    let cursor = Cursor::new(&screenshot);
    let mut loaded_image = image::load(cursor, image::ImageFormat::PNG).unwrap();
    let (width, height) = loaded_image.dimensions();
    debug!("Loaded image dimensions: {} x {}", width, height);
    let cropped = loaded_image.crop(
		element_location.find("x").expect("x").as_f64().expect("numeric x") as u32,
		element_location.find("y").expect("y").as_f64().expect("numeric y") as u32,
		element_size.find("width").expect("width").as_f64().expect("numeric width") as u32,
		element_size.find("height").expect("height").as_f64().expect("numeric height") as u32);

    let mut output_buffer: Vec<u8> = Vec::new();
    cropped.save(&mut output_buffer, image::ImageFormat::PNG).unwrap();

    let extra = extra_fn(session);
    res.set(XExtra(extra));

    res.set(MediaType::Png);
    res.set(ContentLength(output_buffer.len() as u64));
    res.send(output_buffer)
}

fn run(ip: std::net::IpAddr, port: u16, client: chromedriver::Webdriver) {
    let mut server = Nickel::new();
    server.utilize(chromedriver::WebdriverMiddleware::new(client));
    server.post("/streams", streams);
    server.listen((ip, port));
}

fn main() {
    log4rs::init_file("log.toml", Default::default()).unwrap();
    let port = 8000;
    let mut handles = Vec::new();
    for iface in get_if_addrs::get_if_addrs().unwrap() {
        handles.push(::std::thread::spawn(move || {
            let client = chromedriver::Webdriver::new();
            let ip = iface.ip();
            info!("Listening on {}:{} for {}", ip, port, iface.name);
            run(ip, port, client);
        }));
    }

    info!("All listeners spawned");

    for handle in handles {
        handle.join().unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::ast::{Enum, Field, Input, Struct};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Member, PathArguments, Result, Type, Visibility};

pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
    let input = Input::from_syn(node)?;
    input.validate()?;
    Ok(match input {
        Input::Struct(input) => impl_struct(input),
        Input::Enum(input) => impl_enum(input),
    })
}

fn impl_struct(input: Struct) -> TokenStream {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source_body = if input.attrs.transparent.is_some() {
        let only_field = &input.fields[0].member;
        Some(quote! {
            std::error::Error::source(self.#only_field.as_dyn_error())
        })
    } else if let Some(source_field) = input.source_field() {
        let source = &source_field.member;
        let asref = if type_is_option(source_field.ty) {
            Some(quote_spanned!(source.span()=> .as_ref()?))
        } else {
            None
        };
        let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error());
        Some(quote! {
            std::option::Option::Some(#dyn_error)
        })
    } else {
        None
    };
    let source_method = source_body.map(|body| {
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                #body
            }
        }
    });

    let backtrace_method = input.backtrace_field().map(|backtrace_field| {
        let backtrace = &backtrace_field.member;
        let body = if let Some(source_field) = input.source_field() {
            let source = &source_field.member;
            let source_backtrace = if type_is_option(source_field.ty) {
                quote_spanned! {source.span()=>
                    self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace())
                }
            } else {
                quote_spanned! {source.span()=>
                    self.#source.as_dyn_error().backtrace()
                }
            };
            let combinator = if source == backtrace {
                source_backtrace
            } else if type_is_option(backtrace_field.ty) {
                quote! {
                    #source_backtrace.or(self.#backtrace.as_ref())
                }
            } else {
                quote! {
                    std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace))
                }
            };
            quote! {
                use thiserror::private::AsDynError;
                #combinator
            }
        } else if type_is_option(backtrace_field.ty) {
            quote! {
                self.#backtrace.as_ref()
            }
        } else {
            quote! {
                std::option::Option::Some(&self.#backtrace)
            }
        };
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                #body
            }
        }
    });

    let display_body = if input.attrs.transparent.is_some() {
        let only_field = &input.fields[0].member;
        Some(quote! {
            std::fmt::Display::fmt(&self.#only_field, __formatter)
        })
    } else if let Some(display) = &input.attrs.display {
        let use_as_display = if display.has_bonus_display {
            Some(quote! {
                #[allow(unused_imports)]
                use thiserror::private::{DisplayAsDisplay, PathAsDisplay};
            })
        } else {
            None
        };
        let pat = fields_pat(&input.fields);
        Some(quote! {
            #use_as_display
            #[allow(unused_variables, deprecated)]
            let Self #pat = self;
            #display
        })
    } else {
        None
    };
    let display_impl = display_body.map(|body| {
        quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                #[allow(
                    // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
                    clippy::nonstandard_macro_braces,
                    clippy::used_underscore_binding,
                )]
                fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #body
                }
            }
        }
    });

    let from_impl = input.from_field().map(|from_field| {
        let backtrace_field = input.distinct_backtrace_field();
        let from = from_field.ty;
        let body = from_initializer(from_field, backtrace_field);
        quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
                #[allow(deprecated)]
                fn from(source: #from) -> Self {
                    #ty #body
                }
            }
        }
    });

    let error_trait = spanned_error_trait(input.original);

    quote! {
        #[allow(unused_qualifications)]
        impl #impl_generics #error_trait for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display_impl
        #from_impl
    }
}

fn impl_enum(input: Enum) -> TokenStream {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source_method = if input.has_source() {
        let arms = input.variants.iter().map(|variant| {
            let ident = &variant.ident;
            if variant.attrs.transparent.is_some() {
                let only_field = &variant.fields[0].member;
                let source = quote!(std::error::Error::source(transparent.as_dyn_error()));
                quote! {
                    #ty::#ident {#only_field: transparent} => #source,
                }
            } else if let Some(source_field) = variant.source_field() {
                let source = &source_field.member;
                let asref = if type_is_option(source_field.ty) {
                    Some(quote_spanned!(source.span()=> .as_ref()?))
                } else {
                    None
                };
                let varsource = quote!(source);
                let dyn_error = quote_spanned!(source.span()=> #varsource #asref.as_dyn_error());
                quote! {
                    #ty::#ident {#source: #varsource, ..} => std::option::Option::Some(#dyn_error),
                }
            } else {
                quote! {
                    #ty::#ident {..} => std::option::Option::None,
                }
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                #[allow(deprecated)]
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method =
        if input.has_backtrace() {
            let arms = input.variants.iter().map(|variant| {
            let ident = &variant.ident;
            match (variant.backtrace_field(), variant.source_field()) {
                (Some(backtrace_field), Some(source_field))
                    if backtrace_field.attrs.backtrace.is_none() =>
                {
                    let backtrace = &backtrace_field.member;
                    let source = &source_field.member;
                    let varsource = quote!(source);
                    let source_backtrace = if type_is_option(source_field.ty) {
                        quote_spanned! {source.span()=>
                            #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace())
                        }
                    } else {
                        quote_spanned! {source.span()=>
                            #varsource.as_dyn_error().backtrace()
                        }
                    };
                    let combinator = if type_is_option(backtrace_field.ty) {
                        quote! {
                            #source_backtrace.or(backtrace.as_ref())
                        }
                    } else {
                        quote! {
                            std::option::Option::Some(#source_backtrace.unwrap_or(backtrace))
                        }
                    };
                    quote! {
                        #ty::#ident {
                            #backtrace: backtrace,
                            #source: #varsource,
                            ..
                        } => {
                            use thiserror::private::AsDynError;
                            #combinator
                        }
                    }
                }
                (Some(backtrace_field), _) => {
                    let backtrace = &backtrace_field.member;
                    if variant.from_field().map_or(false, |f| f.member == *backtrace) {
                        let varsource = quote!(source);
                        let source_backtrace = quote_spanned! {backtrace.span()=>
                            #varsource.as_dyn_error().backtrace()
                        };
                        quote! {
                            #ty::#ident {#backtrace: #varsource, ..} => {
                                use thiserror::private::AsDynError;
                                #source_backtrace
                            }
                        }
                    } else {
                        let body = if type_is_option(backtrace_field.ty) {
                            quote!(backtrace.as_ref())
                        } else {
                            quote!(std::option::Option::Some(backtrace))
                        };
                        quote! {
                            #ty::#ident {#backtrace: backtrace, ..} => #body,
                        }
                    }
                }
                (None, _) => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
            Some(quote! {
                fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                    #[allow(deprecated)]
                    match self {
                        #(#arms)*
                    }
                }
            })
        } else {
            None
        };

    let display_impl = if input.has_display() {
        let use_as_display = if input.variants.iter().any(|v| {
            v.attrs
                .display
                .as_ref()
                .map_or(false, |display| display.has_bonus_display)
        }) {
            Some(quote! {
                #[allow(unused_imports)]
                use thiserror::private::{DisplayAsDisplay, PathAsDisplay};
            })
        } else {
            None
        };
        let void_deref = if input.variants.is_empty() {
            Some(quote!(*))
        } else {
            None
        };
        let arms = input.variants.iter().map(|variant| {
            let display = match &variant.attrs.display {
                Some(display) => display.to_token_stream(),
                None => {
                    let only_field = match &variant.fields[0].member {
                        Member::Named(ident) => ident.clone(),
                        Member::Unnamed(index) => format_ident!("_{}", index),
                    };
                    quote!(std::fmt::Display::fmt(#only_field, __formatter))
                }
            };
            let ident = &variant.ident;
            let pat = fields_pat(&variant.fields);
            quote! {
                #ty::#ident #pat => #display
            }
        });
        Some(quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #use_as_display
                    #[allow(
                        unused_variables,
                        deprecated,
                        // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
                        clippy::nonstandard_macro_braces,
                        clippy::used_underscore_binding,
                    )]
                    match #void_deref self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    let from_impls = input.variants.iter().filter_map(|variant| {
        let from_field = variant.from_field()?;
        let backtrace_field = variant.distinct_backtrace_field();
        let variant = &variant.ident;
        let from = from_field.ty;
        let body = from_initializer(from_field, backtrace_field);
        Some(quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
                #[allow(deprecated)]
                fn from(source: #from) -> Self {
                    #ty::#variant #body
                }
            }
        })
    });

    let error_trait = spanned_error_trait(input.original);

    quote! {
        #[allow(unused_qualifications)]
        impl #impl_generics #error_trait for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display_impl
        #(#from_impls)*
    }
}

fn fields_pat(fields: &[Field]) -> TokenStream {
    let mut members = fields.iter().map(|field| &field.member).peekable();
    match members.peek() {
        Some(Member::Named(_)) => quote!({ #(#members),* }),
        Some(Member::Unnamed(_)) => {
            let vars = members.map(|member| match member {
                Member::Unnamed(member) => format_ident!("_{}", member),
                Member::Named(_) => unreachable!(),
            });
            quote!((#(#vars),*))
        }
        None => quote!({}),
    }
}

fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream {
    let from_member = &from_field.member;
    let backtrace = backtrace_field.map(|backtrace_field| {
        let backtrace_member = &backtrace_field.member;
        if type_is_option(backtrace_field.ty) {
            quote! {
                #backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()),
            }
        } else {
            quote! {
                #backtrace_member: std::convert::From::from(std::backtrace::Backtrace::capture()),
            }
        }
    });
    quote!({
        #from_member: source,
        #backtrace
    })
}

fn type_is_option(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    if last.ident != "Option" {
        return false;
    }

    match &last.arguments {
        PathArguments::AngleBracketed(bracketed) => bracketed.args.len() == 1,
        _ => false,
    }
}

fn spanned_error_trait(input: &DeriveInput) -> TokenStream {
    let vis_span = match &input.vis {
        Visibility::Public(vis) => Some(vis.pub_token.span()),
        Visibility::Crate(vis) => Some(vis.crate_token.span()),
        Visibility::Restricted(vis) => Some(vis.pub_token.span()),
        Visibility::Inherited => None,
    };
    let data_span = match &input.data {
        Data::Struct(data) => data.struct_token.span(),
        Data::Enum(data) => data.enum_token.span(),
        Data::Union(data) => data.union_token.span(),
    };
    let first_span = vis_span.unwrap_or(data_span);
    let last_span = input.ident.span();
    let path = quote_spanned!(first_span=> std::error::);
    let error = quote_spanned!(last_span=> Error);
    quote!(#path #error)
}
<commit_msg>Handle enum containing #[source] #[backtrace] field<commit_after>use crate::ast::{Enum, Field, Input, Struct};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Member, PathArguments, Result, Type, Visibility};

pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
    let input = Input::from_syn(node)?;
    input.validate()?;
    Ok(match input {
        Input::Struct(input) => impl_struct(input),
        Input::Enum(input) => impl_enum(input),
    })
}

fn impl_struct(input: Struct) -> TokenStream {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source_body = if input.attrs.transparent.is_some() {
        let only_field = &input.fields[0].member;
        Some(quote! {
            std::error::Error::source(self.#only_field.as_dyn_error())
        })
    } else if let Some(source_field) = input.source_field() {
        let source = &source_field.member;
        let asref = if type_is_option(source_field.ty) {
            Some(quote_spanned!(source.span()=> .as_ref()?))
        } else {
            None
        };
        let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error());
        Some(quote! {
            std::option::Option::Some(#dyn_error)
        })
    } else {
        None
    };
    let source_method = source_body.map(|body| {
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                #body
            }
        }
    });

    let backtrace_method = input.backtrace_field().map(|backtrace_field| {
        let backtrace = &backtrace_field.member;
        let body = if let Some(source_field) = input.source_field() {
            let source = &source_field.member;
            let source_backtrace = if type_is_option(source_field.ty) {
                quote_spanned! {source.span()=>
                    self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace())
                }
            } else {
                quote_spanned! {source.span()=>
                    self.#source.as_dyn_error().backtrace()
                }
            };
            let combinator = if source == backtrace {
                source_backtrace
            } else if type_is_option(backtrace_field.ty) {
                quote! {
                    #source_backtrace.or(self.#backtrace.as_ref())
                }
            } else {
                quote! {
                    std::option::Option::Some(#source_backtrace.unwrap_or(&self.#backtrace))
                }
            };
            quote! {
                use thiserror::private::AsDynError;
                #combinator
            }
        } else if type_is_option(backtrace_field.ty) {
            quote! {
                self.#backtrace.as_ref()
            }
        } else {
            quote! {
                std::option::Option::Some(&self.#backtrace)
            }
        };
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                #body
            }
        }
    });

    let display_body = if input.attrs.transparent.is_some() {
        let only_field = &input.fields[0].member;
        Some(quote! {
            std::fmt::Display::fmt(&self.#only_field, __formatter)
        })
    } else if let Some(display) = &input.attrs.display {
        let use_as_display = if display.has_bonus_display {
            Some(quote! {
                #[allow(unused_imports)]
                use thiserror::private::{DisplayAsDisplay, PathAsDisplay};
            })
        } else {
            None
        };
        let pat = fields_pat(&input.fields);
        Some(quote! {
            #use_as_display
            #[allow(unused_variables, deprecated)]
            let Self #pat = self;
            #display
        })
    } else {
        None
    };
    let display_impl = display_body.map(|body| {
        quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                #[allow(
                    // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
                    clippy::nonstandard_macro_braces,
                    clippy::used_underscore_binding,
                )]
                fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #body
                }
            }
        }
    });

    let from_impl = input.from_field().map(|from_field| {
        let backtrace_field = input.distinct_backtrace_field();
        let from = from_field.ty;
        let body = from_initializer(from_field, backtrace_field);
        quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
                #[allow(deprecated)]
                fn from(source: #from) -> Self {
                    #ty #body
                }
            }
        }
    });

    let error_trait = spanned_error_trait(input.original);

    quote! {
        #[allow(unused_qualifications)]
        impl #impl_generics #error_trait for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display_impl
        #from_impl
    }
}

fn impl_enum(input: Enum) -> TokenStream {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source_method = if input.has_source() {
        let arms = input.variants.iter().map(|variant| {
            let ident = &variant.ident;
            if variant.attrs.transparent.is_some() {
                let only_field = &variant.fields[0].member;
                let source = quote!(std::error::Error::source(transparent.as_dyn_error()));
                quote! {
                    #ty::#ident {#only_field: transparent} => #source,
                }
            } else if let Some(source_field) = variant.source_field() {
                let source = &source_field.member;
                let asref = if type_is_option(source_field.ty) {
                    Some(quote_spanned!(source.span()=> .as_ref()?))
                } else {
                    None
                };
                let varsource = quote!(source);
                let dyn_error = quote_spanned!(source.span()=> #varsource #asref.as_dyn_error());
                quote! {
                    #ty::#ident {#source: #varsource, ..} => std::option::Option::Some(#dyn_error),
                }
            } else {
                quote! {
                    #ty::#ident {..} => std::option::Option::None,
                }
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                #[allow(deprecated)]
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method =
        if input.has_backtrace() {
            let arms = input.variants.iter().map(|variant| {
            let ident = &variant.ident;
            match (variant.backtrace_field(), variant.source_field()) {
                (Some(backtrace_field), Some(source_field))
                    if backtrace_field.attrs.backtrace.is_none() =>
                {
                    let backtrace = &backtrace_field.member;
                    let source = &source_field.member;
                    let varsource = quote!(source);
                    let source_backtrace = if type_is_option(source_field.ty) {
                        quote_spanned! {source.span()=>
                            #varsource.as_ref().and_then(|source| source.as_dyn_error().backtrace())
                        }
                    } else {
                        quote_spanned! {source.span()=>
                            #varsource.as_dyn_error().backtrace()
                        }
                    };
                    let combinator = if type_is_option(backtrace_field.ty) {
                        quote! {
                            #source_backtrace.or(backtrace.as_ref())
                        }
                    } else {
                        quote! {
                            std::option::Option::Some(#source_backtrace.unwrap_or(backtrace))
                        }
                    };
                    quote! {
                        #ty::#ident {
                            #backtrace: backtrace,
                            #source: #varsource,
                            ..
                        } => {
                            use thiserror::private::AsDynError;
                            #combinator
                        }
                    }
                }
                (Some(backtrace_field), _) => {
                    let backtrace = &backtrace_field.member;
                    if variant.source_field().map_or(false, |f| f.member == *backtrace) {
                        let varsource = quote!(source);
                        let source_backtrace = quote_spanned! {backtrace.span()=>
                            #varsource.as_dyn_error().backtrace()
                        };
                        quote! {
                            #ty::#ident {#backtrace: #varsource, ..} => {
                                use thiserror::private::AsDynError;
                                #source_backtrace
                            }
                        }
                    } else {
                        let body = if type_is_option(backtrace_field.ty) {
                            quote!(backtrace.as_ref())
                        } else {
                            quote!(std::option::Option::Some(backtrace))
                        };
                        quote! {
                            #ty::#ident {#backtrace: backtrace, ..} => #body,
                        }
                    }
                }
                (None, _) => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
            Some(quote! {
                fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                    #[allow(deprecated)]
                    match self {
                        #(#arms)*
                    }
                }
            })
        } else {
            None
        };

    let display_impl = if input.has_display() {
        let use_as_display = if input.variants.iter().any(|v| {
            v.attrs
                .display
                .as_ref()
                .map_or(false, |display| display.has_bonus_display)
        }) {
            Some(quote! {
                #[allow(unused_imports)]
                use thiserror::private::{DisplayAsDisplay, PathAsDisplay};
            })
        } else {
            None
        };
        let void_deref = if input.variants.is_empty() {
            Some(quote!(*))
        } else {
            None
        };
        let arms = input.variants.iter().map(|variant| {
            let display = match &variant.attrs.display {
                Some(display) => display.to_token_stream(),
                None => {
                    let only_field = match &variant.fields[0].member {
                        Member::Named(ident) => ident.clone(),
                        Member::Unnamed(index) => format_ident!("_{}", index),
                    };
                    quote!(std::fmt::Display::fmt(#only_field, __formatter))
                }
            };
            let ident = &variant.ident;
            let pat = fields_pat(&variant.fields);
            quote! {
                #ty::#ident #pat => #display
            }
        });
        Some(quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #use_as_display
                    #[allow(
                        unused_variables,
                        deprecated,
                        // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
                        clippy::nonstandard_macro_braces,
                        clippy::used_underscore_binding,
                    )]
                    match #void_deref self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    let from_impls = input.variants.iter().filter_map(|variant| {
        let from_field = variant.from_field()?;
        let backtrace_field = variant.distinct_backtrace_field();
        let variant = &variant.ident;
        let from = from_field.ty;
        let body = from_initializer(from_field, backtrace_field);
        Some(quote! {
            #[allow(unused_qualifications)]
            impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause {
                #[allow(deprecated)]
                fn from(source: #from) -> Self {
                    #ty::#variant #body
                }
            }
        })
    });

    let error_trait = spanned_error_trait(input.original);

    quote! {
        #[allow(unused_qualifications)]
        impl #impl_generics #error_trait for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display_impl
        #(#from_impls)*
    }
}

fn fields_pat(fields: &[Field]) -> TokenStream {
    let mut members = fields.iter().map(|field| &field.member).peekable();
    match members.peek() {
        Some(Member::Named(_)) => quote!({ #(#members),* }),
        Some(Member::Unnamed(_)) => {
            let vars = members.map(|member| match member {
                Member::Unnamed(member) => format_ident!("_{}", member),
                Member::Named(_) => unreachable!(),
            });
            quote!((#(#vars),*))
        }
        None => quote!({}),
    }
}

fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream {
    let from_member = &from_field.member;
    let backtrace = backtrace_field.map(|backtrace_field| {
        let backtrace_member = &backtrace_field.member;
        if type_is_option(backtrace_field.ty) {
            quote! {
                #backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()),
            }
        } else {
            quote! {
                #backtrace_member: std::convert::From::from(std::backtrace::Backtrace::capture()),
            }
        }
    });
    quote!({
        #from_member: source,
        #backtrace
    })
}

fn type_is_option(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    if last.ident != "Option" {
        return false;
    }

    match &last.arguments {
        PathArguments::AngleBracketed(bracketed) => bracketed.args.len() == 1,
        _ => false,
    }
}

fn spanned_error_trait(input: &DeriveInput) -> TokenStream {
    let vis_span = match &input.vis {
        Visibility::Public(vis) => Some(vis.pub_token.span()),
        Visibility::Crate(vis) => Some(vis.crate_token.span()),
        Visibility::Restricted(vis) => Some(vis.pub_token.span()),
        Visibility::Inherited => None,
    };
    let data_span = match &input.data {
        Data::Struct(data) => data.struct_token.span(),
        Data::Enum(data) => data.enum_token.span(),
        Data::Union(data) => data.union_token.span(),
    };
    let first_span = vis_span.unwrap_or(data_span);
    let last_span = input.ident.span();
    let path = quote_spanned!(first_span=> std::error::);
    let error = quote_spanned!(last_span=> Error);
    quote!(#path #error)
}
<|endoftext|>


<commit_before>// Copyright (c) 2018-2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::api::EncoderConfig;
use crate::api::InterConfig;
use crate::frame::*;
use crate::util::CastFromPrimitive;
use crate::util::Pixel;

use crate::hawktracer::*;

use std::cmp;
use std::collections::BTreeSet;
use std::sync::Arc;

/// Runs keyframe detection on frames from the lookahead queue.
pub(crate) struct SceneChangeDetector {
  /// Minimum average difference between YUV deltas that will trigger a scene change.
  threshold: u8,
  /// Fast scene cut detection mode, ignoing chroma planes.
  fast_mode: bool,
  /// Frames that cannot be marked as keyframes due to the algorithm excluding them.
  /// Storing the frame numbers allows us to avoid looking back more than one frame.
  excluded_frames: BTreeSet<u64>,
}

impl SceneChangeDetector {
  pub fn new(bit_depth: u8, fast_mode: bool) -> Self {
    // This implementation is based on a Python implementation at
    // https://pyscenedetect.readthedocs.io/en/latest/reference/detection-methods/.
    // The Python implementation uses HSV values and a threshold of 30. Comparing the
    // YUV values was sufficient in most cases, and avoided a more costly YUV->RGB->HSV
    // conversion, but the deltas needed to be scaled down. The deltas for keyframes
    // in YUV were about 1/3 to 1/2 of what they were in HSV, but non-keyframes were
    // very unlikely to have a delta greater than 3 in YUV, whereas they may reach into
    // the double digits in HSV. Therefore, 12 was chosen as a reasonable default threshold.
    // This may be adjusted later.
    const BASE_THRESHOLD: u8 = 12;
    Self {
      threshold: BASE_THRESHOLD * bit_depth / 8,
      fast_mode,
      excluded_frames: BTreeSet::new(),
    }
  }

  /// Runs keyframe detection on the next frame in the lookahead queue.
  ///
  /// This function requires that a subset of input frames
  /// is passed to it in order, and that `keyframes` is only
  /// updated from this method. `input_frameno` should correspond
  /// to the second frame in `frame_set`.
  ///
  /// This will gracefully handle the first frame in the video as well.
  #[hawktracer(analyze_next_frame)]
  pub fn analyze_next_frame<T: Pixel>(
    &mut self, frame_set: &[Arc<Frame<T>>], input_frameno: u64,
    config: &EncoderConfig, inter_cfg: &InterConfig,
    keyframes: &BTreeSet<u64>,
  ) -> bool {
    if !config.speed_settings.no_scene_detection {
      self.exclude_scene_flashes(&frame_set, input_frameno, inter_cfg);
    }

    self.is_key_frame(
      &frame_set[0],
      &frame_set[1],
      input_frameno,
      config,
      keyframes,
    )
  }

  /// Determines if `current_frame` should be a keyframe.
  fn is_key_frame<T: Pixel>(
    &self, previous_frame: &Frame<T>, current_frame: &Frame<T>,
    current_frameno: u64, config: &EncoderConfig,
    keyframes: &BTreeSet<u64>,
  ) -> bool {
    // Find the distance to the previous keyframe.
    let previous_keyframe = keyframes.iter().last().unwrap();
    let distance = current_frameno - previous_keyframe;

    // Handle minimum and maximum key frame intervals.
    if distance < config.min_key_frame_interval {
      return false;
    }
    if distance >= config.max_key_frame_interval {
      return true;
    }

    // Skip smart scene detection if it's disabled
    if config.speed_settings.no_scene_detection {
      return false;
    }

    if self.excluded_frames.contains(&current_frameno) {
      return false;
    }

    self.has_scenecut(previous_frame, current_frame)
  }

  /// Uses lookahead to avoid coding short flashes as scenecuts.
  /// Saves excluded frame numbers in `self.excluded_frames`.
  fn exclude_scene_flashes<T: Pixel>(
    &mut self, frame_subset: &[Arc<Frame<T>>], frameno: u64,
    inter_cfg: &InterConfig,
  ) {
    let lookahead_distance = cmp::min(
      inter_cfg.keyframe_lookahead_distance() as usize,
      frame_subset.len() - 1,
    );

    // Where A and B are scenes: AAAAAABBBAAAAAA
    // If BBB is shorter than lookahead_distance, it is detected as a flash
    // and not considered a scenecut.
    for j in 1..=lookahead_distance {
      if !self.has_scenecut(&frame_subset[0], &frame_subset[j]) {
        // Any frame in between `0` and `j` cannot be a real scenecut.
        for i in 0..=j {
          let frameno = frameno + i as u64 - 1;
          self.excluded_frames.insert(frameno);
        }
      }
    }

    // Where A-F are scenes: AAAAABBCCDDEEFFFFFF
    // If each of BB ... EE are shorter than `lookahead_distance`, they are
    // detected as flashes and not considered scenecuts.
    // Instead, the first F frame becomes a scenecut.
    // If the video ends before F, no frame becomes a scenecut.
    for i in 1..lookahead_distance {
      if self.has_scenecut(&frame_subset[i], &frame_subset[lookahead_distance])
      {
        // If the current frame is the frame before a scenecut, it cannot also be the frame of a scenecut.
        let frameno = frameno + i as u64 - 1;
        self.excluded_frames.insert(frameno);
      }
    }
  }

  /// Run a comparison between two frames to determine if they qualify for a scenecut.
  ///
  /// The current algorithm detects fast cuts using changes in colour and intensity between frames.
  /// Since the difference between frames is used, only fast cuts are detected
  /// with this method. This is intended to change via https://github.com/xiph/rav1e/issues/794.
  fn has_scenecut<T: Pixel>(
    &self, frame1: &Frame<T>, frame2: &Frame<T>,
  ) -> bool {
    let mut len = frame2.planes[0].cfg.width * frame2.planes[0].cfg.height;
    let mut delta = 0;

    delta += self.delta_in_planes(&frame1.planes[0], &frame2.planes[0]);

    if !self.fast_mode {
      let u_dec = frame1.planes[1].cfg.xdec + frame1.planes[1].cfg.ydec;
      len +=
        (frame2.planes[1].cfg.width * frame2.planes[1].cfg.height) << u_dec;

      delta +=
        self.delta_in_planes(&frame1.planes[1], &frame2.planes[1]) << u_dec;

      let v_dec = frame1.planes[2].cfg.xdec + frame1.planes[2].cfg.ydec;
      len +=
        (frame2.planes[2].cfg.width * frame2.planes[2].cfg.height) << v_dec;
      delta +=
        self.delta_in_planes(&frame1.planes[2], &frame2.planes[2]) << v_dec;
    }

    delta >= self.threshold as u64 * len as u64
  }

  fn delta_in_planes<T: Pixel>(
    &self, plane1: &Plane<T>, plane2: &Plane<T>,
  ) -> u64 {
    let mut delta = 0;
    let lines = plane1.rows_iter().zip(plane2.rows_iter());

    for (l1, l2) in lines {
      let delta_line = l1
        .iter()
        .zip(l2.iter())
        .map(|(&p1, &p2)| {
          (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
        })
        .sum::<u64>();
      delta += delta_line;
    }
    delta
  }
}
<commit_msg>Simplify further the scenechange logic<commit_after>// Copyright (c) 2018-2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use crate::api::EncoderConfig;
use crate::api::InterConfig;
use crate::frame::*;
use crate::util::CastFromPrimitive;
use crate::util::Pixel;

use crate::hawktracer::*;

use std::cmp;
use std::collections::BTreeSet;
use std::sync::Arc;

/// Runs keyframe detection on frames from the lookahead queue.
pub(crate) struct SceneChangeDetector {
  /// Minimum average difference between YUV deltas that will trigger a scene change.
  threshold: u8,
  /// Fast scene cut detection mode, ignoing chroma planes.
  fast_mode: bool,
  /// Frames that cannot be marked as keyframes due to the algorithm excluding them.
  /// Storing the frame numbers allows us to avoid looking back more than one frame.
  excluded_frames: BTreeSet<u64>,
}

impl SceneChangeDetector {
  pub fn new(bit_depth: u8, fast_mode: bool) -> Self {
    // This implementation is based on a Python implementation at
    // https://pyscenedetect.readthedocs.io/en/latest/reference/detection-methods/.
    // The Python implementation uses HSV values and a threshold of 30. Comparing the
    // YUV values was sufficient in most cases, and avoided a more costly YUV->RGB->HSV
    // conversion, but the deltas needed to be scaled down. The deltas for keyframes
    // in YUV were about 1/3 to 1/2 of what they were in HSV, but non-keyframes were
    // very unlikely to have a delta greater than 3 in YUV, whereas they may reach into
    // the double digits in HSV. Therefore, 12 was chosen as a reasonable default threshold.
    // This may be adjusted later.
    const BASE_THRESHOLD: u8 = 12;
    Self {
      threshold: BASE_THRESHOLD * bit_depth / 8,
      fast_mode,
      excluded_frames: BTreeSet::new(),
    }
  }

  /// Runs keyframe detection on the next frame in the lookahead queue.
  ///
  /// This function requires that a subset of input frames
  /// is passed to it in order, and that `keyframes` is only
  /// updated from this method. `input_frameno` should correspond
  /// to the second frame in `frame_set`.
  ///
  /// This will gracefully handle the first frame in the video as well.
  #[hawktracer(analyze_next_frame)]
  pub fn analyze_next_frame<T: Pixel>(
    &mut self, frame_set: &[Arc<Frame<T>>], input_frameno: u64,
    config: &EncoderConfig, inter_cfg: &InterConfig,
    keyframes: &BTreeSet<u64>,
  ) -> bool {
    let previous_keyframe = keyframes.iter().last().unwrap();
    // Find the distance to the previous keyframe.
    let distance = input_frameno - previous_keyframe;

    // Handle minimum and maximum key frame intervals.
    if distance < config.min_key_frame_interval {
      return false;
    }
    if distance >= config.max_key_frame_interval {
      return true;
    }

    if config.speed_settings.no_scene_detection {
      return false;
    }

    self.exclude_scene_flashes(&frame_set, input_frameno, inter_cfg);

    self.is_key_frame(
      &frame_set[0],
      &frame_set[1],
      input_frameno,
    )
  }

  /// Determines if `current_frame` should be a keyframe.
  fn is_key_frame<T: Pixel>(
    &self, previous_frame: &Frame<T>, current_frame: &Frame<T>,
    current_frameno: u64,
  ) -> bool {
    if self.excluded_frames.contains(&current_frameno) {
      return false;
    }

    self.has_scenecut(previous_frame, current_frame)
  }

  /// Uses lookahead to avoid coding short flashes as scenecuts.
  /// Saves excluded frame numbers in `self.excluded_frames`.
  fn exclude_scene_flashes<T: Pixel>(
    &mut self, frame_subset: &[Arc<Frame<T>>], frameno: u64,
    inter_cfg: &InterConfig,
  ) {
    let lookahead_distance = cmp::min(
      inter_cfg.keyframe_lookahead_distance() as usize,
      frame_subset.len() - 1,
    );

    // Where A and B are scenes: AAAAAABBBAAAAAA
    // If BBB is shorter than lookahead_distance, it is detected as a flash
    // and not considered a scenecut.
    for j in 1..=lookahead_distance {
      if !self.has_scenecut(&frame_subset[0], &frame_subset[j]) {
        // Any frame in between `0` and `j` cannot be a real scenecut.
        for i in 0..=j {
          let frameno = frameno + i as u64 - 1;
          self.excluded_frames.insert(frameno);
        }
      }
    }

    // Where A-F are scenes: AAAAABBCCDDEEFFFFFF
    // If each of BB ... EE are shorter than `lookahead_distance`, they are
    // detected as flashes and not considered scenecuts.
    // Instead, the first F frame becomes a scenecut.
    // If the video ends before F, no frame becomes a scenecut.
    for i in 1..lookahead_distance {
      if self.has_scenecut(&frame_subset[i], &frame_subset[lookahead_distance])
      {
        // If the current frame is the frame before a scenecut, it cannot also be the frame of a scenecut.
        let frameno = frameno + i as u64 - 1;
        self.excluded_frames.insert(frameno);
      }
    }
  }

  /// Run a comparison between two frames to determine if they qualify for a scenecut.
  ///
  /// The current algorithm detects fast cuts using changes in colour and intensity between frames.
  /// Since the difference between frames is used, only fast cuts are detected
  /// with this method. This is intended to change via https://github.com/xiph/rav1e/issues/794.
  fn has_scenecut<T: Pixel>(
    &self, frame1: &Frame<T>, frame2: &Frame<T>,
  ) -> bool {
    let mut len = frame2.planes[0].cfg.width * frame2.planes[0].cfg.height;
    let mut delta = 0;

    delta += self.delta_in_planes(&frame1.planes[0], &frame2.planes[0]);

    if !self.fast_mode {
      let u_dec = frame1.planes[1].cfg.xdec + frame1.planes[1].cfg.ydec;
      len +=
        (frame2.planes[1].cfg.width * frame2.planes[1].cfg.height) << u_dec;

      delta +=
        self.delta_in_planes(&frame1.planes[1], &frame2.planes[1]) << u_dec;

      let v_dec = frame1.planes[2].cfg.xdec + frame1.planes[2].cfg.ydec;
      len +=
        (frame2.planes[2].cfg.width * frame2.planes[2].cfg.height) << v_dec;
      delta +=
        self.delta_in_planes(&frame1.planes[2], &frame2.planes[2]) << v_dec;
    }

    delta >= self.threshold as u64 * len as u64
  }

  fn delta_in_planes<T: Pixel>(
    &self, plane1: &Plane<T>, plane2: &Plane<T>,
  ) -> u64 {
    let mut delta = 0;
    let lines = plane1.rows_iter().zip(plane2.rows_iter());

    for (l1, l2) in lines {
      let delta_line = l1
        .iter()
        .zip(l2.iter())
        .map(|(&p1, &p2)| {
          (i16::cast_from(p1) - i16::cast_from(p2)).abs() as u64
        })
        .sum::<u64>();
      delta += delta_line;
    }
    delta
  }
}
<|endoftext|>


<commit_before>extern crate yaml_rust;
extern crate argparse;
#[macro_use] extern crate log;
#[macro_use] extern crate from_variants;

extern crate serde;
extern crate serde_json;
extern crate serde_yaml;

#[macro_use] extern crate serde_derive;

#[derive(Serialize, Deserialize)]
enum ArgValue {
    None,
    Str(String),
    Bool(bool),
    Byte(u8),
    Int16(i16),
    Int32(i32),
    Int64(i64),
    UInt16(u16),
    UInt32(u32),
    UInt64(u64),
    Double(f64),
}

#[derive(Serialize, Deserialize)]
enum DbusMessageType {
    Method,
    Signal,
}

#[derive(Serialize, Deserialize)]
struct DbusMessage {
    api_name: String,
    dbus_name: String,
    message_type: DbusMessageType,
    args: Vec<(String,ArgValue)>,
}

#[derive(Serialize, Deserialize)]
struct DbusMessageRouting(String, String, String, Vec<DbusMessage>);

#[derive(Serialize, Deserialize)]
struct ConfYaml(Vec<DbusMessageRouting>);

fn main()
{
    let conf = vec![
        DbusMessageRouting(
            "com.a.b.servicetest".to_string(),
            "/com/a/b/servicetest".to_string(),
            "com.a.b.IServiceTest".to_string(),
            vec![ DbusMessage { api_name: "api_name".to_string(),
                                dbus_name: "dbus_name".to_string(),
                                message_type: DbusMessageType::Method,
                                args: vec![
                                    ("param1".to_string(), ArgValue::Str("Hi".to_string())),
                                    ("param2".to_string(), ArgValue::Bool(true)),
                                    ("param3".to_string(), ArgValue::Int64(17i64)),
                                    ("param4".to_string(), ArgValue::Double(2.718281828f64)),
                                ]
                              }
            ]
        )
    ];

    let s = serde_yaml::to_string(&conf).unwrap();
    println!("s : {}", s);

}
<commit_msg>json serialization sample.<commit_after>extern crate yaml_rust;
extern crate argparse;
#[macro_use] extern crate log;
#[macro_use] extern crate from_variants;

extern crate serde;
extern crate serde_json;
extern crate serde_yaml;

#[macro_use] extern crate serde_derive;

#[derive(Serialize, Deserialize)]
enum ArgValue {
    None,
    Str(String),
    Bool(bool),
    Byte(u8),
    Int16(i16),
    Int32(i32),
    Int64(i64),
    UInt16(u16),
    UInt32(u32),
    UInt64(u64),
    Double(f64),
}

#[derive(Serialize, Deserialize)]
enum DbusMessageType {
    Method,
    Signal,
}

type NamedArg = (String,ArgValue);

#[derive(Serialize, Deserialize)]
struct DbusMessage {
    api_name: String,
    dbus_name: String,
    message_type: DbusMessageType,
    args: Vec<NamedArg>,
}

#[derive(Serialize, Deserialize)]
struct DbusMessageRouting(String, String, String, Vec<DbusMessage>);

#[derive(Serialize, Deserialize)]
struct ConfYaml(Vec<DbusMessageRouting>);

#[derive(Serialize, Deserialize)]
struct ClientRequest(DbusMessageType, String, Vec<NamedArg>);

fn main()
{
    let conf = vec![
        DbusMessageRouting(
            "com.a.b.servicetest".to_string(),
            "/com/a/b/servicetest".to_string(),
            "com.a.b.IServiceTest".to_string(),
            vec![ DbusMessage { api_name: "api_name".to_string(),
                                dbus_name: "dbus_name".to_string(),
                                message_type: DbusMessageType::Method,
                                args: vec![
                                    ("param1".to_string(), ArgValue::Str("Hi".to_string())),
                                    ("param2".to_string(), ArgValue::Bool(true)),
                                    ("param3".to_string(), ArgValue::Int64(17i64)),
                                    ("param4".to_string(), ArgValue::Double(2.718281828f64)),
                                ]
                              }
            ]
        )
    ];

    let s = serde_yaml::to_string(&conf).unwrap();
    println!("s : {}", s);

    let r = ClientRequest(DbusMessageType::Method, "api_name".to_string(), vec![
                                        ("param1".to_string(), ArgValue::Str("Hi".to_string())),
                                        ("param2".to_string(), ArgValue::Bool(true)),
                                        ("param3".to_string(), ArgValue::Int64(17i64)),
                                        ("param4".to_string(), ArgValue::Double(2.718281828f64)),
                                ]
                          );
    let rs = serde_json::to_string(&r).unwrap();
    println!("rs : {}", rs);

}
<|endoftext|>