<commit_before>// src/controllers/transactions_controller.rs

/// Transactions Controller

// Import
// External
use ::nickel::{JsonBody, QueryString, Request};
use ::bson::{Bson};
use ::bson::oid::ObjectId;
use ::chrono::{Datelike, DateTime, Duration, Local, Timelike, TimeZone, UTC};
use ::chrono::{NaiveDate, NaiveDateTime};
// Utilities
use ::common::api_result::ApiResult;
use ::common::config::Config;
use ::common::data_access::ServerData;
use ::common::session as Session;
// Models
use ::models::transaction_model::{TransactionModel, PubTransactionModel};
// DAO
use ::dao::dao_manager::DAOManager;

#[derive(Clone)]
pub struct TransactionsController{
    dao_manager: DAOManager,
    config: Config
}

impl TransactionsController{

    pub fn new(dao_manager: DAOManager, config: Config) -> TransactionsController{
        TransactionsController{
            dao_manager: dao_manager,
            config: config
        }
    }

    /// Find All Transactions belonging to a money map account
    /// Utilize query params to filter results. Defaults to transactions for current month.
    /// Query params:
    ///     start_date - inclusive (format: YYYYMMDD)
    ///     end_date - exclusive (format: YYYYMMDD)
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    /// `mm_id` - String Money Map ID
    /// `acc_id` - String Account ID
    ///
    /// # Returns
    /// `ApiResult<Vec<PubTransactionModel>>` - ApiResult including a vector of transactions
    pub fn find(&self, req: &mut Request<ServerData>, mm_id: String, acc_id: String) -> ApiResult<Vec<PubTransactionModel>, ()>{
        let START_TIME = "00:00:00";

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        // Get Query Params
        let query = req.query();

        // Start Date
        let start_date_prop = query.get("start_date");
        let mut start_date = if let Some(date) = start_date_prop{
            let sd = [date, START_TIME].concat();
            match UTC.datetime_from_str(&sd, "%Y%m%d%T"){
                Ok(datetime) => Some(datetime),
                Err(e) => {
                    error!("{}",e);
                    return ApiResult::Failure{msg:"Unable to parse start date."};
                }
            }
        }else{
            None
        };

        // End Date
        let end_date_prop = query.get("end_date");
        let mut end_date = if let Some(date) = end_date_prop{
            let edt = [date, START_TIME].concat();
            match UTC.datetime_from_str(&edt, "%Y%m%d%T"){
                Ok(datetime) => Some(datetime),
                Err(e) => {
                    error!("{}",e);
                    return ApiResult::Failure{msg:"Unable to parse end date."};
                }
            }
        }else{
            None
        };

        if let None = start_date{
            if let None = end_date{
                // Start and End dates not supplied
                // Default start/end filter to current month
                let now = UTC::now();

                // Determine first day of this month
                start_date = now.with_day(1).unwrap().with_hour(0).unwrap().with_minute(0).unwrap().with_second(0).unwrap().with_nanosecond(0);

                // Determine first day of next month
                let (y, m) = if now.month() == 12 { (now.year() + 1, 1) } else { (now.year(), now.month() + 1) };
                end_date = now.with_year(y).unwrap().with_month(m).unwrap().with_day(1).unwrap().with_hour(0).unwrap().with_minute(0).unwrap().with_second(0).unwrap().with_nanosecond(0);
            }
        }

        // START Retrieve DAO ---------------------------------------------------------------------
        match self.dao_manager.get_transaction_dao(){
            Ok(transaction_dao) => {
                // END Retrieve DAO ---------------------------------------------------------------

                match ObjectId::with_string(&user_id){
                    Ok(user_obj_id) => {
                        match ObjectId::with_string(&mm_id){
                            Ok(mm_obj_id) => {
                                match ObjectId::with_string(&acc_id){
                                    Ok(acc_obj_id) => {

                                        // Verify Account is valid and user has permission
                                        if transaction_dao.is_valid_account(user_obj_id, mm_obj_id.clone(), acc_obj_id.clone()){

                                            // Get list of transactions for account
                                            let transactions = transaction_dao.find(mm_obj_id, acc_obj_id, start_date, end_date);
                                            ApiResult::Success{
                                                result:transactions.into_iter().map(|x| PubTransactionModel::new(x)).collect()
                                            }

                                        }else{
                                            ApiResult::Failure{msg:"Failed to find transactions. Invalid account."}
                                        }
                                    },
                                    Err(e) => {
                                        error!("{}", e);
                                        ApiResult::Failure{msg:"Failed to find transactions. Invalid account ID."}
                                    }
                                }
                            },
                            Err(e) => {
                                error!("{}", e);
                                ApiResult::Failure{msg:"Failed to find transactions. Invalid money map ID."}
                            }
                        }
                    },
                    Err(e) => {
                        error!("{}", e);
                        ApiResult::Failure{msg:"Failed to find transactions. Invalid user ID."}
                    }
                }

                // START Retrieve DAO Error Handling ----------------------------------------------
            },
            Err(e) => {
                error!("{}",e.get_message().to_string());
                ApiResult::Failure{msg:"Unable to interact with database"}
            }
        }
        // END Retrieve DAO Error Handling --------------------------------------------------------
    }// end find_all

    /// Create Transaction
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    /// `mm_id` - String Money Map ID
    /// `acc_id` - String Account ID
    ///
    /// # Returns
    /// `ApiResult<PubTransactionModel>` - ApiResult including the created transaction
    pub fn create(&self, req: &mut Request<ServerData>, mm_id: String, acc_id: String) -> ApiResult<PubTransactionModel, PubTransactionModel>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        // START Retrieve DAO ---------------------------------------------------------------------
        match self.dao_manager.get_transaction_dao(){
            Ok(transaction_dao) => {
                // END Retrieve DAO ---------------------------------------------------------------

                match ObjectId::with_string(&user_id){
                    Ok(user_obj_id) => {
                        match ObjectId::with_string(&mm_id){
                            Ok(mm_obj_id) => {
                                match ObjectId::with_string(&acc_id){
                                    Ok(acc_obj_id) => {

                                        // Verify Account is valid to receive transactions and user has permission
                                        if transaction_dao.is_valid_account(user_obj_id, mm_obj_id.clone(), acc_obj_id.clone()){

                                            // Parse body to PubTransactionModel
                                            match req.json_as::<PubTransactionModel>(){
                                                //TODO Parse error! Floating point numbers are being parsed poorly. Eg. 0.12 -> 0.119999999999999
                                                // This only occurs when the decimal is sent in the request body json as a number, not as a string.
                                                // Temporary workaround: require transaction amount to be passed as a string
                                                Ok(mut pub_transaction) => {
                                                    pub_transaction.set_money_map_id(Some(mm_obj_id));
                                                    pub_transaction.set_account_id(Some(acc_obj_id));

                                                    // Validate
                                                    let validation_result = pub_transaction.validate();
                                                    if validation_result.is_valid(){
                                                        // Save Transaction
                                                        match transaction_dao.create(&pub_transaction){
                                                            Ok(new_transaction) => {
                                                                ApiResult::Success{result:PubTransactionModel::new(new_transaction)}
                                                            },
                                                            Err(e) => {
                                                                error!("{}",e);
                                                                ApiResult::Failure{msg:"Unable to create account"}
                                                            }
                                                        }
                                                    }else{
                                                        ApiResult::Invalid{validation:validation_result, request:pub_transaction}
                                                    }
                                                },
                                                Err(e) => {
                                                    error!("{}",e);
                                                    ApiResult::Failure{msg:"Invalid format. Unable to parse body."}
                                                }
                                            }

                                        }else{
                                            ApiResult::Failure{msg:"Failed to create transaction. Invalid account."}
                                        }

                                    },
                                    Err(e) => {
                                        error!("{}", e);
                                        ApiResult::Failure{msg:"Failed to create transaction. Invalid account ID."}
                                    }
                                }
                            },
                            Err(e) => {
                                error!("{}", e);
                                ApiResult::Failure{msg:"Failed to create transaction. Invalid money map ID."}
                            }
                        }
                    },
                    Err(e) => {
                        error!("{}", e);
                        ApiResult::Failure{msg:"Failed to create transaction. Invalid user ID."}
                    }
                }

                // START Retrieve DAO Error Handling ----------------------------------------------
            },
            Err(e) => {
                error!("{}",e.get_message().to_string());
                ApiResult::Failure{msg:"Unable to interact with database"}
            }
        }
        // END Retrieve DAO Error Handling --------------------------------------------------------
    }// end create

    /// Modify TransactionModel
    /// Transaction Amount and Type (credit/debit) can only be modified during transaction month
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    ///
    /// # Returns
    /// `ApiResult<PubTransactionModel>` - ApiResult including the modified transaction
    pub fn modify(&self, req: &mut Request<ServerData>) -> ApiResult<PubTransactionModel, PubTransactionModel>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        ApiResult::Failure{msg:"Method not implemented"}
    }// end modify

    /// Delete a Transaction
    ///
    /// # Arguments
    /// `self`
    /// `req` - &nickel::Request
    ///
    /// # Returns
    /// `ApiResult<String>` - ApiResult
    pub fn delete(&self, req: &Request<ServerData>) -> ApiResult<String, ()>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        ApiResult::Failure{msg:"Method not implemented"}
    }// end delete
}
<commit_msg>Fix find transaction default end_date<commit_after>// src/controllers/transactions_controller.rs

/// Transactions Controller

// Import
// External
use ::nickel::{JsonBody, QueryString, Request};
use ::bson::{Bson};
use ::bson::oid::ObjectId;
use ::chrono::{Datelike, DateTime, Duration, Local, Timelike, TimeZone, UTC};
use ::chrono::{NaiveDate, NaiveDateTime};
// Utilities
use ::common::api_result::ApiResult;
use ::common::config::Config;
use ::common::data_access::ServerData;
use ::common::session as Session;
// Models
use ::models::transaction_model::{TransactionModel, PubTransactionModel};
// DAO
use ::dao::dao_manager::DAOManager;

#[derive(Clone)]
pub struct TransactionsController{
    dao_manager: DAOManager,
    config: Config
}

impl TransactionsController{

    pub fn new(dao_manager: DAOManager, config: Config) -> TransactionsController{
        TransactionsController{
            dao_manager: dao_manager,
            config: config
        }
    }

    /// Find All Transactions belonging to a money map account
    /// Utilize query params to filter results. Defaults to transactions for current month.
    /// Query params:
    ///     start_date - inclusive (format: YYYYMMDD)
    ///     end_date - exclusive (format: YYYYMMDD)
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    /// `mm_id` - String Money Map ID
    /// `acc_id` - String Account ID
    ///
    /// # Returns
    /// `ApiResult<Vec<PubTransactionModel>>` - ApiResult including a vector of transactions
    pub fn find(&self, req: &mut Request<ServerData>, mm_id: String, acc_id: String) -> ApiResult<Vec<PubTransactionModel>, ()>{
        let START_TIME = "00:00:00";

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        // Get Query Params
        let query = req.query();

        // Start Date
        let start_date_prop = query.get("start_date");
        let mut start_date = if let Some(date) = start_date_prop{
            let sd = [date, START_TIME].concat();
            match UTC.datetime_from_str(&sd, "%Y%m%d%T"){
                Ok(datetime) => Some(datetime),
                Err(e) => {
                    error!("{}",e);
                    return ApiResult::Failure{msg:"Unable to parse start date."};
                }
            }
        }else{
            None
        };

        // End Date
        let end_date_prop = query.get("end_date");
        let mut end_date = if let Some(date) = end_date_prop{
            let edt = [date, START_TIME].concat();
            match UTC.datetime_from_str(&edt, "%Y%m%d%T"){
                Ok(datetime) => Some(datetime),
                Err(e) => {
                    error!("{}",e);
                    return ApiResult::Failure{msg:"Unable to parse end date."};
                }
            }
        }else{
            None
        };

        if let None = start_date{
            if let None = end_date{
                // Start and End dates not supplied
                // Default start/end filter to current month
                let now = UTC::now();

                // Determine first day of this month
                start_date = now.with_day(1).unwrap().with_hour(0).unwrap().with_minute(0).unwrap().with_second(0).unwrap().with_nanosecond(0);
                // Determine first day of next month
                let (y, m) = if now.month() == 12 { (now.year() + 1, 1) } else { (now.year(), now.month() + 1) };
                end_date = now.with_year(y).unwrap().with_day(1).unwrap().with_month(m).unwrap().with_hour(0).unwrap().with_minute(0).unwrap().with_second(0).unwrap().with_nanosecond(0);
            }
        }

        // START Retrieve DAO ---------------------------------------------------------------------
        match self.dao_manager.get_transaction_dao(){
            Ok(transaction_dao) => {
                // END Retrieve DAO ---------------------------------------------------------------

                match ObjectId::with_string(&user_id){
                    Ok(user_obj_id) => {
                        match ObjectId::with_string(&mm_id){
                            Ok(mm_obj_id) => {
                                match ObjectId::with_string(&acc_id){
                                    Ok(acc_obj_id) => {

                                        // Verify Account is valid and user has permission
                                        if transaction_dao.is_valid_account(user_obj_id, mm_obj_id.clone(), acc_obj_id.clone()){

                                            // Get list of transactions for account
                                            let transactions = transaction_dao.find(mm_obj_id, acc_obj_id, start_date, end_date);
                                            ApiResult::Success{
                                                result:transactions.into_iter().map(|x| PubTransactionModel::new(x)).collect()
                                            }

                                        }else{
                                            ApiResult::Failure{msg:"Failed to find transactions. Invalid account."}
                                        }
                                    },
                                    Err(e) => {
                                        error!("{}", e);
                                        ApiResult::Failure{msg:"Failed to find transactions. Invalid account ID."}
                                    }
                                }
                            },
                            Err(e) => {
                                error!("{}", e);
                                ApiResult::Failure{msg:"Failed to find transactions. Invalid money map ID."}
                            }
                        }
                    },
                    Err(e) => {
                        error!("{}", e);
                        ApiResult::Failure{msg:"Failed to find transactions. Invalid user ID."}
                    }
                }

                // START Retrieve DAO Error Handling ----------------------------------------------
            },
            Err(e) => {
                error!("{}",e.get_message().to_string());
                ApiResult::Failure{msg:"Unable to interact with database"}
            }
        }
        // END Retrieve DAO Error Handling --------------------------------------------------------
    }// end find_all

    /// Create Transaction
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    /// `mm_id` - String Money Map ID
    /// `acc_id` - String Account ID
    ///
    /// # Returns
    /// `ApiResult<PubTransactionModel>` - ApiResult including the created transaction
    pub fn create(&self, req: &mut Request<ServerData>, mm_id: String, acc_id: String) -> ApiResult<PubTransactionModel, PubTransactionModel>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        // START Retrieve DAO ---------------------------------------------------------------------
        match self.dao_manager.get_transaction_dao(){
            Ok(transaction_dao) => {
                // END Retrieve DAO ---------------------------------------------------------------

                match ObjectId::with_string(&user_id){
                    Ok(user_obj_id) => {
                        match ObjectId::with_string(&mm_id){
                            Ok(mm_obj_id) => {
                                match ObjectId::with_string(&acc_id){
                                    Ok(acc_obj_id) => {

                                        // Verify Account is valid to receive transactions and user has permission
                                        if transaction_dao.is_valid_account(user_obj_id, mm_obj_id.clone(), acc_obj_id.clone()){

                                            // Parse body to PubTransactionModel
                                            match req.json_as::<PubTransactionModel>(){
                                                //TODO Parse error! Floating point numbers are being parsed poorly. Eg. 0.12 -> 0.119999999999999
                                                // This only occurs when the decimal is sent in the request body json as a number, not as a string.
                                                // Temporary workaround: require transaction amount to be passed as a string
                                                Ok(mut pub_transaction) => {
                                                    pub_transaction.set_money_map_id(Some(mm_obj_id));
                                                    pub_transaction.set_account_id(Some(acc_obj_id));

                                                    // Validate
                                                    let validation_result = pub_transaction.validate();
                                                    if validation_result.is_valid(){
                                                        // Save Transaction
                                                        match transaction_dao.create(&pub_transaction){
                                                            Ok(new_transaction) => {
                                                                ApiResult::Success{result:PubTransactionModel::new(new_transaction)}
                                                            },
                                                            Err(e) => {
                                                                error!("{}",e);
                                                                ApiResult::Failure{msg:"Unable to create account"}
                                                            }
                                                        }
                                                    }else{
                                                        ApiResult::Invalid{validation:validation_result, request:pub_transaction}
                                                    }
                                                },
                                                Err(e) => {
                                                    error!("{}",e);
                                                    ApiResult::Failure{msg:"Invalid format. Unable to parse body."}
                                                }
                                            }

                                        }else{
                                            ApiResult::Failure{msg:"Failed to create transaction. Invalid account."}
                                        }

                                    },
                                    Err(e) => {
                                        error!("{}", e);
                                        ApiResult::Failure{msg:"Failed to create transaction. Invalid account ID."}
                                    }
                                }
                            },
                            Err(e) => {
                                error!("{}", e);
                                ApiResult::Failure{msg:"Failed to create transaction. Invalid money map ID."}
                            }
                        }
                    },
                    Err(e) => {
                        error!("{}", e);
                        ApiResult::Failure{msg:"Failed to create transaction. Invalid user ID."}
                    }
                }

                // START Retrieve DAO Error Handling ----------------------------------------------
            },
            Err(e) => {
                error!("{}",e.get_message().to_string());
                ApiResult::Failure{msg:"Unable to interact with database"}
            }
        }
        // END Retrieve DAO Error Handling --------------------------------------------------------
    }// end create

    /// Modify TransactionModel
    /// Transaction Amount and Type (credit/debit) can only be modified during transaction month
    ///
    /// # Arguments
    /// `self`
    /// `req` - nickel::Request
    ///
    /// # Returns
    /// `ApiResult<PubTransactionModel>` - ApiResult including the modified transaction
    pub fn modify(&self, req: &mut Request<ServerData>) -> ApiResult<PubTransactionModel, PubTransactionModel>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        ApiResult::Failure{msg:"Method not implemented"}
    }// end modify

    /// Delete a Transaction
    ///
    /// # Arguments
    /// `self`
    /// `req` - &nickel::Request
    ///
    /// # Returns
    /// `ApiResult<String>` - ApiResult
    pub fn delete(&self, req: &Request<ServerData>) -> ApiResult<String, ()>{

        let user_id = match Session::get_session_id(req){
            Ok(id) => id,
            Err(e) => {
                error!("{}",e.get_message().to_string());
                return ApiResult::Failure{msg:"Unable to retrieve session data."};
            }
        };

        ApiResult::Failure{msg:"Method not implemented"}
    }// end delete
}
<|endoftext|>


<commit_before>use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(&visitor.mbcx.body);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_> {
    fn remove_never_initialized_mut_locals(&mut self, into: &Place<'_>) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        self.never_initialized_mut_locals.remove(&into.local);
    }
}

impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
    fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, _location: Location) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            }
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            }
            _ => {}
        }
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) {
        if let StatementKind::Assign(box (into, _)) = &statement.kind {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                    never_initialized_mut_locals={:?}",
                statement, into.local, self.never_initialized_mut_locals
            );
            self.remove_never_initialized_mut_locals(into);
        }
    }

    fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Some(user_local) = path.place.as_local() {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<commit_msg>Use Place directly on remove_never_initialized_mut_locals, it's Copy<commit_after>use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(&visitor.mbcx.body);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_> {
    fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        self.never_initialized_mut_locals.remove(&into.local);
    }
}

impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
    fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, _location: Location) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(*into);
            }
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(*location);
            }
            _ => {}
        }
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) {
        if let StatementKind::Assign(box (into, _)) = &statement.kind {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                    never_initialized_mut_locals={:?}",
                statement, into.local, self.never_initialized_mut_locals
            );
            self.remove_never_initialized_mut_locals(*into);
        }
    }

    fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Some(user_local) = path.place.as_local() {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate gdk;
extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gdk::{Cursor, CursorType, WindowExt};

use gtk::prelude::*;

use media::{ContextMessage, PlaybackContext};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, StreamsController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Ready,
    Seeking {
        switch_to_play: bool,
        keep_paused: bool,
    },
    Stopped,
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    open_export_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,
    streams_ctrl: Rc<RefCell<StreamsController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            open_export_btn: builder.get_object("export_toc-btn").unwrap(),

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),
            streams_ctrl: StreamsController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.open_export_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
            StreamsController::register_callbacks(&this_mut.streams_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    pub fn play_pause(&mut self) {
        let mut context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    AudioController::register_tick_callback(&self.audio_ctrl);
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    AudioController::remove_tick_callback(&self.audio_ctrl);
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                self.state = match self.state {
                    ControllerState::Seeking {
                        switch_to_play,
                        keep_paused,
                    } => ControllerState::Seeking {
                        switch_to_play: switch_to_play,
                        keep_paused: keep_paused,
                    },
                    ControllerState::EOS | ControllerState::Ready => ControllerState::Seeking {
                        switch_to_play: true,
                        keep_paused: false,
                    },
                    ControllerState::Paused => ControllerState::Seeking {
                        switch_to_play: false,
                        keep_paused: true,
                    },
                    _ => ControllerState::Seeking {
                        switch_to_play: false,
                        keep_paused: false,
                    },
                };

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            AudioController::register_tick_callback(&self.audio_ctrl);
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    pub fn refresh_info(&mut self, position: u64) {
        self.info_ctrl.borrow_mut().tick(position, false);
    }

    pub fn select_streams(&mut self, stream_ids: &[String]) {
        self.context
            .as_ref()
            .expect("MainController::select_streams no context")
            .select_streams(stream_ids);
    }

    fn hold(&mut self) {
        self.switch_to_busy();
        AudioController::remove_tick_callback(&self.audio_ctrl);
        self.play_pause_btn.set_icon_name("media-playback-start");

        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn set_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.switch_to_default();
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking {
                                switch_to_play,
                                keep_paused,
                            } => {
                                if switch_to_play {
                                    this.context
                                        .as_mut()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    AudioController::register_tick_callback(&this.audio_ctrl);
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();
                        let mut context = this.context
                            .take()
                            .expect("MainController(InitDone) no context available");

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController(InitDone) failed to lock info")
                            .streams
                            .video_selected
                            .is_some();

                        this.header_bar
                            .set_subtitle(Some(context.file_name.as_str()));
                        this.streams_ctrl.borrow_mut().new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.video_ctrl.new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);
                        this.export_ctrl.borrow_mut().new_media(&context);

                        this.set_context(context);

                        this.state = ControllerState::Ready;
                    }
                    StreamsSelected => {
                        let mut this = this_rc.borrow_mut();
                        let mut context = this.context
                            .take()
                            .expect("MainController(StreamsSelected) no context available");
                        {
                            let info = context
                                .info
                                .lock()
                                .expect("MainController(StreamsSelected) failed to lock info");

                            this.info_ctrl.borrow().streams_changed(&info);
                        }
                        this.set_context(context);
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                                this.seek(pos_to_restore, true); // accurate
                            }
                            _ => {
                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tick callback will be register again in case of a seek
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();
                        this.context = None;
                        this.state = ControllerState::Stopped;
                        this.switch_to_default();

                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.remove_listener();
                AudioController::remove_tick_callback(&this.audio_ctrl);
            }

            glib::Continue(keep_going)
        }));
    }

    fn switch_to_busy(&mut self) {
        self.window.set_sensitive(false);

        let gdk_window = self.window.get_window().unwrap();
        gdk_window.set_cursor(&Cursor::new_for_display(
            &gdk_window.get_display(),
            CursorType::Watch,
        ));
    }

    fn switch_to_default(&mut self) {
        self.window.get_window().unwrap().set_cursor(None);
        self.window.set_sensitive(true);
    }

    fn select_media(&mut self) {
        self.switch_to_busy();

        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            if self.context.is_some() {
                self.state = ControllerState::Paused;
            }
            self.switch_to_default();
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.video_ctrl.cleanup();
        self.export_ctrl.borrow_mut().cleanup();
        self.streams_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");

        let (ctx_tx, ui_rx) = channel();

        self.state = ControllerState::Stopped;
        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
            }
            Err(error) => {
                self.switch_to_default();
                eprintln!("Error opening media: {}", error);
            }
        };
    }
}
<commit_msg>Avoid briefly returning to previous chapter<commit_after>extern crate gdk;
extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gdk::{Cursor, CursorType, WindowExt};

use gtk::prelude::*;

use media::{ContextMessage, PlaybackContext};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, StreamsController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Ready,
    Seeking {
        switch_to_play: bool,
        keep_paused: bool,
    },
    Stopped,
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    open_export_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,
    streams_ctrl: Rc<RefCell<StreamsController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            open_export_btn: builder.get_object("export_toc-btn").unwrap(),

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),
            streams_ctrl: StreamsController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.open_export_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
            StreamsController::register_callbacks(&this_mut.streams_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    pub fn play_pause(&mut self) {
        let mut context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    AudioController::register_tick_callback(&self.audio_ctrl);
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    AudioController::remove_tick_callback(&self.audio_ctrl);
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                self.state = match self.state {
                    ControllerState::Seeking {
                        switch_to_play,
                        keep_paused,
                    } => ControllerState::Seeking {
                        switch_to_play: switch_to_play,
                        keep_paused: keep_paused,
                    },
                    ControllerState::EOS | ControllerState::Ready => ControllerState::Seeking {
                        switch_to_play: true,
                        keep_paused: false,
                    },
                    ControllerState::Paused => ControllerState::Seeking {
                        switch_to_play: false,
                        keep_paused: true,
                    },
                    _ => ControllerState::Seeking {
                        switch_to_play: false,
                        keep_paused: false,
                    },
                };

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            AudioController::register_tick_callback(&self.audio_ctrl);
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    pub fn refresh_info(&mut self, position: u64) {
        match self.state {
            ControllerState::Seeking { .. } => (),
            _ => self.info_ctrl.borrow_mut().tick(position, false),
        }
    }

    pub fn select_streams(&mut self, stream_ids: &[String]) {
        self.context
            .as_ref()
            .expect("MainController::select_streams no context")
            .select_streams(stream_ids);
    }

    fn hold(&mut self) {
        self.switch_to_busy();
        AudioController::remove_tick_callback(&self.audio_ctrl);
        self.play_pause_btn.set_icon_name("media-playback-start");

        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn set_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.switch_to_default();
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking {
                                switch_to_play,
                                keep_paused,
                            } => {
                                if switch_to_play {
                                    this.context
                                        .as_mut()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    AudioController::register_tick_callback(&this.audio_ctrl);
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();
                        let mut context = this.context
                            .take()
                            .expect("MainController(InitDone) no context available");

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController(InitDone) failed to lock info")
                            .streams
                            .video_selected
                            .is_some();

                        this.header_bar
                            .set_subtitle(Some(context.file_name.as_str()));
                        this.streams_ctrl.borrow_mut().new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.video_ctrl.new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);
                        this.export_ctrl.borrow_mut().new_media(&context);

                        this.set_context(context);

                        this.state = ControllerState::Ready;
                    }
                    StreamsSelected => {
                        let mut this = this_rc.borrow_mut();
                        let mut context = this.context
                            .take()
                            .expect("MainController(StreamsSelected) no context available");
                        {
                            let info = context
                                .info
                                .lock()
                                .expect("MainController(StreamsSelected) failed to lock info");

                            this.info_ctrl.borrow().streams_changed(&info);
                        }
                        this.set_context(context);
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                                this.seek(pos_to_restore, true); // accurate
                            }
                            _ => {
                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tick callback will be register again in case of a seek
                                AudioController::remove_tick_callback(&this.audio_ctrl);
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();
                        this.context = None;
                        this.state = ControllerState::Stopped;
                        this.switch_to_default();

                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.remove_listener();
                AudioController::remove_tick_callback(&this.audio_ctrl);
            }

            glib::Continue(keep_going)
        }));
    }

    fn switch_to_busy(&mut self) {
        self.window.set_sensitive(false);

        let gdk_window = self.window.get_window().unwrap();
        gdk_window.set_cursor(&Cursor::new_for_display(
            &gdk_window.get_display(),
            CursorType::Watch,
        ));
    }

    fn switch_to_default(&mut self) {
        self.window.get_window().unwrap().set_cursor(None);
        self.window.set_sensitive(true);
    }

    fn select_media(&mut self) {
        self.switch_to_busy();

        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            if self.context.is_some() {
                self.state = ControllerState::Paused;
            }
            self.switch_to_default();
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.video_ctrl.cleanup();
        self.export_ctrl.borrow_mut().cleanup();
        self.streams_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");

        let (ctx_tx, ui_rx) = channel();

        self.state = ControllerState::Stopped;
        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
            }
            Err(error) => {
                self.switch_to_default();
                eprintln!("Error opening media: {}", error);
            }
        };
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ops;
use std::ptr;
use std::slice;

use nodrop::NoDrop;

use num_traits::{Unsigned, Zero, One};

pub struct OverflowError<T1, T2>(pub T1, pub T2);

pub enum InsertError<T1, T2> {
    Overflow(T1, T2),
    OutOfBounds(T1, T2)
}

pub unsafe trait Array {
    type Item;
    type Index: Index;
    fn as_ptr(&self) -> *const Self::Item;
    fn as_mut_ptr(&mut self) -> *mut Self::Item;
    fn capacity(&self) -> usize;
}

pub trait Index : Unsigned + ops::AddAssign + ops::SubAssign + Copy {
    fn as_usize(self) -> usize;
    fn from_usize(value: usize) -> Self;
}

macro_rules! impl_index {
    ($index_type:ty) => (
        impl Index for $index_type {
            fn as_usize(self) -> usize {
                self as usize
            }

            fn from_usize(value: usize) -> Self {
                value as $index_type
            }
        }
    )
}

impl_index!(u8);
impl_index!(u16);
impl_index!(u32);
impl_index!(usize);

macro_rules! impl_array {
    ($index_type:ty, $len:expr ) => (
        unsafe impl<T> Array for [T; $len] {
            type Item = T;
            type Index = $index_type;
            #[inline(always)]
            fn as_ptr(&self) -> *const T {
                self as *const _ as *const _
            }

            #[inline(always)]
            fn as_mut_ptr(&mut self) -> *mut T {
                self as *mut _ as *mut _
            }

            #[inline(always)]
            fn capacity(&self) -> usize {
                $len
            }
        }
    )
}

macro_rules! impl_array_many {
    ($index_type:ty, $($len:expr),+) => (
        $(
            impl_array!($index_type, $len);
        )*
    );
}

impl_array_many!(u8, 1, 2, 7, 31, 256);

pub struct LockstepArray<A1: Array, A2: Array> {
    len: A1::Index,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>
}

impl<A1, A2> LockstepArray<A1, A2> where A1: Array, A2: Array {
    pub fn new() -> LockstepArray<A1, A2> {
        LockstepArray {
            len: Zero::zero(),
            array1: NoDrop::new(unsafe {mem::uninitialized()}),
            array2: NoDrop::new(unsafe {mem::uninitialized()})
        }
    }

    pub fn from_arrays<B1, B2>(array1: B1, array2: B2) -> LockstepArray<A1, A2>
        where B1: Array<Item=A1::Item>, B2: Array<Item=A2::Item> {
        let mut lockstep = LockstepArray::<A1, A2>::new();
        // Eventually these will be encoded into the type system
        assert!(array1.capacity() <= lockstep.array1.capacity());
        assert!(array2.capacity() <= lockstep.array2.capacity());
        assert_eq!(array1.capacity(), array2.capacity());
        // TODO Implement from_arrays, requires unsafe code
        unsafe {
            ptr::copy_nonoverlapping(array1.as_ptr(),
                                     lockstep.array1.as_mut_ptr(),
                                     array1.capacity());
            mem::forget(array1);
            ptr::copy_nonoverlapping(array2.as_ptr(),
                                     lockstep.array2.as_mut_ptr(),
                                     array2.capacity());
            mem::forget(array2);
        }
        lockstep
    }

    pub fn push(&mut self, item1: A1::Item, item2: A2::Item)
                -> Result<(), OverflowError<A1::Item, A2::Item>> {
        if self.len.as_usize() == self.array1.capacity() {
            return Err(OverflowError(item1, item2));
        }
        unsafe {
            let p1 = self.array1.as_mut_ptr();
            ptr::write(p1.offset(self.len.as_usize() as isize), item1);
            let p2 = self.array2.as_mut_ptr();
            ptr::write(p2.offset(self.len.as_usize() as isize), item2);
        }
        self.len += One::one();
        Ok(())
    }

    pub fn pop(&mut self) -> Option<(A1::Item, A2::Item)> {
        if self.len == Zero::zero() {
            None
        } else {
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item1 = ptr::read(src1);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item2 = ptr::read(src2);
                // Update length
                self.len -= One::one();
                Some((item1, item2))
            }
        }
    }

    pub fn insert(&mut self, index: usize, item1: A1::Item, item2: A2::Item)
                  -> Result<(), InsertError<A1::Item, A2::Item>> {
        if index >= self.len.as_usize() {
            Err(InsertError::OutOfBounds(item1, item2))
        } else if self.len.as_usize() == self.array1.capacity() {
            Err(InsertError::Overflow(item1, item2))
        } else {
            unsafe {
                // TODO: Tricky math, check indices and such
                let dest1 = self.array1.as_mut_ptr().offset(index as isize);
                ptr::copy(dest1, dest1.offset(1), self.len.as_usize() - index);
                ptr::write(dest1, item1);
                let dest2 = self.array2.as_mut_ptr().offset(index as isize);
                ptr::copy(dest2, dest2.offset(1), self.len.as_usize() - index);
                ptr::write(dest2, item2);
            }
            self.len += One::one();
            Ok(())
        }
    }

    pub fn remove(&mut self, index: usize) -> Option<(A1::Item, A2::Item)> {
        if index >= self.len.as_usize() {
            None
        } else {
            let item1;
            let item2;
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item1 = ptr::read(src1);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src1.offset(1), src1, self.len.as_usize() - index);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item2 = ptr::read(src2);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src2, src2.offset(1), self.len.as_usize() - index);
                // Update length
                self.len -= One::one();
            }
            Some((item1, item2))
        }
    }

    pub fn is_empty(&self) -> bool {
        return self.len != Zero::zero()
    }

    pub fn len(&self) -> usize {
        return self.len.as_usize()
    }

    pub fn array1(&self) -> &[A1::Item] {
        unsafe {
            let ptr = self.array1.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array2(&self) -> &[A2::Item] {
        unsafe {
            let ptr = self.array2.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array1_mut(&mut self) -> &mut [A1::Item] {
        unsafe {
            let ptr = self.array1.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn array2_mut(&mut self) -> &mut [A2::Item] {
        unsafe {
            let ptr = self.array2.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn capacity(&self) -> usize {
        self.array1.capacity()
    }
}

impl<A1, A2> Default for LockstepArray<A1, A2> where A1: Array, A2: Array {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>Correct at end of lockstep array<commit_after>use std::mem;
use std::ops;
use std::ptr;
use std::slice;

use nodrop::NoDrop;

use num_traits::{Unsigned, Zero, One};

pub struct OverflowError<T1, T2>(pub T1, pub T2);

pub enum InsertError<T1, T2> {
    Overflow(T1, T2),
    OutOfBounds(T1, T2)
}

pub unsafe trait Array {
    type Item;
    type Index: Index;
    fn as_ptr(&self) -> *const Self::Item;
    fn as_mut_ptr(&mut self) -> *mut Self::Item;
    fn capacity(&self) -> usize;
}

pub trait Index : Unsigned + ops::AddAssign + ops::SubAssign + Copy {
    fn as_usize(self) -> usize;
    fn from_usize(value: usize) -> Self;
}

macro_rules! impl_index {
    ($index_type:ty) => (
        impl Index for $index_type {
            fn as_usize(self) -> usize {
                self as usize
            }

            fn from_usize(value: usize) -> Self {
                value as $index_type
            }
        }
    )
}

impl_index!(u8);
impl_index!(u16);
impl_index!(u32);
impl_index!(usize);

macro_rules! impl_array {
    ($index_type:ty, $len:expr ) => (
        unsafe impl<T> Array for [T; $len] {
            type Item = T;
            type Index = $index_type;
            #[inline(always)]
            fn as_ptr(&self) -> *const T {
                self as *const _ as *const _
            }

            #[inline(always)]
            fn as_mut_ptr(&mut self) -> *mut T {
                self as *mut _ as *mut _
            }

            #[inline(always)]
            fn capacity(&self) -> usize {
                $len
            }
        }
    )
}

macro_rules! impl_array_many {
    ($index_type:ty, $($len:expr),+) => (
        $(
            impl_array!($index_type, $len);
        )*
    );
}

impl_array_many!(u8, 1, 2, 7, 31, 256);

pub struct LockstepArray<A1: Array, A2: Array> {
    len: A1::Index,
    array1: NoDrop<A1>,
    array2: NoDrop<A2>
}

impl<A1, A2> LockstepArray<A1, A2> where A1: Array, A2: Array {
    pub fn new() -> LockstepArray<A1, A2> {
        LockstepArray {
            len: Zero::zero(),
            array1: NoDrop::new(unsafe {mem::uninitialized()}),
            array2: NoDrop::new(unsafe {mem::uninitialized()})
        }
    }

    pub fn from_arrays<B1, B2>(array1: B1, array2: B2) -> LockstepArray<A1, A2>
        where B1: Array<Item=A1::Item>, B2: Array<Item=A2::Item> {
        let mut lockstep = LockstepArray::<A1, A2>::new();
        // Eventually these will be encoded into the type system
        assert!(array1.capacity() <= lockstep.array1.capacity());
        assert!(array2.capacity() <= lockstep.array2.capacity());
        assert_eq!(array1.capacity(), array2.capacity());
        // TODO Implement from_arrays, requires unsafe code
        unsafe {
            ptr::copy_nonoverlapping(array1.as_ptr(),
                                     lockstep.array1.as_mut_ptr(),
                                     array1.capacity());
            mem::forget(array1);
            ptr::copy_nonoverlapping(array2.as_ptr(),
                                     lockstep.array2.as_mut_ptr(),
                                     array2.capacity());
            mem::forget(array2);
        }
        lockstep
    }

    pub fn push(&mut self, item1: A1::Item, item2: A2::Item)
                -> Result<(), OverflowError<A1::Item, A2::Item>> {
        if self.len.as_usize() == self.array1.capacity() {
            return Err(OverflowError(item1, item2));
        }
        unsafe {
            let p1 = self.array1.as_mut_ptr();
            ptr::write(p1.offset(self.len.as_usize() as isize), item1);
            let p2 = self.array2.as_mut_ptr();
            ptr::write(p2.offset(self.len.as_usize() as isize), item2);
        }
        self.len += One::one();
        Ok(())
    }

    pub fn pop(&mut self) -> Option<(A1::Item, A2::Item)> {
        if self.len == Zero::zero() {
            None
        } else {
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item1 = ptr::read(src1);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr()
                    .offset(self.len.as_usize() as isize);
                // Move item 1 out of the pointer
                let item2 = ptr::read(src2);
                // Update length
                self.len -= One::one();
                Some((item1, item2))
            }
        }
    }

    pub fn insert(&mut self, index: usize, item1: A1::Item, item2: A2::Item)
                  -> Result<(), InsertError<A1::Item, A2::Item>> {
        if index > self.len.as_usize() {
            Err(InsertError::OutOfBounds(item1, item2))
        } else if self.len.as_usize() == self.array1.capacity() {
            Err(InsertError::Overflow(item1, item2))
        } else {
            unsafe {
                // TODO: Tricky math, check indices and such
                let dest1 = self.array1.as_mut_ptr().offset(index as isize);
                ptr::copy(dest1, dest1.offset(1), self.len.as_usize() - index);
                ptr::write(dest1, item1);
                let dest2 = self.array2.as_mut_ptr().offset(index as isize);
                ptr::copy(dest2, dest2.offset(1), self.len.as_usize() - index);
                ptr::write(dest2, item2);
            }
            self.len += One::one();
            Ok(())
        }
    }

    pub fn remove(&mut self, index: usize) -> Option<(A1::Item, A2::Item)> {
        if index >= self.len.as_usize() {
            None
        } else {
            let item1;
            let item2;
            unsafe {
                // Calculate item 1's pointer
                let src1 = self.array1.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item1 = ptr::read(src1);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src1.offset(1), src1, self.len.as_usize() - index);
                // Calculate item 1's pointer
                let src2 = self.array2.as_mut_ptr().offset(index as isize);
                // Move item 1 out of the pointer
                item2 = ptr::read(src2);
                // Shift down array 1 down by 1
                // Is self.len - index correct?
                ptr::copy(src2, src2.offset(1), self.len.as_usize() - index);
                // Update length
                self.len -= One::one();
            }
            Some((item1, item2))
        }
    }

    pub fn is_empty(&self) -> bool {
        return self.len != Zero::zero()
    }

    pub fn len(&self) -> usize {
        return self.len.as_usize()
    }

    pub fn array1(&self) -> &[A1::Item] {
        unsafe {
            let ptr = self.array1.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array2(&self) -> &[A2::Item] {
        unsafe {
            let ptr = self.array2.as_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts(ptr, len)
        }
    }

    pub fn array1_mut(&mut self) -> &mut [A1::Item] {
        unsafe {
            let ptr = self.array1.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn array2_mut(&mut self) -> &mut [A2::Item] {
        unsafe {
            let ptr = self.array2.as_mut_ptr();
            let len = self.len.as_usize();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    pub fn capacity(&self) -> usize {
        self.array1.capacity()
    }
}

impl<A1, A2> Default for LockstepArray<A1, A2> where A1: Array, A2: Array {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>use std::ops::Index;
use std::rc::Rc;

#[derive(Clone)]
pub struct Dataset<T: Clone> {
    value: Rc<DatasetIntf<T, Output = T>>,
}
impl<T: Clone> Dataset<T> {
    pub fn len(&self) -> usize {
        self.value.len()
    }
    pub fn iter(&self) -> Box<Iterator<Item = T>> {
        self.value.iter()
    }
}

pub trait DatasetIntf<T>: Index<usize, Output = T> {
    fn len(&self) -> usize;
    fn iter(&self) -> Box<Iterator<Item = T>>;
}
<commit_msg>add constructor for Dataset<commit_after>use std::rc::Rc;
use std::ops::Index;

#[derive(Clone)]
pub struct Dataset<T: Clone> {
    value: Rc<DatasetIntf<T, Output = T>>,
}
impl<T: Clone> Dataset<T> {
    pub fn new(arg: Rc<DatasetIntf<T, Output = T>>) -> Self {
        Dataset { value: arg }
    }
    pub fn len(&self) -> usize {
        self.value.len()
    }
    pub fn iter(&self) -> Box<Iterator<Item = T>> {
        self.value.iter()
    }
}

pub trait DatasetIntf<T>: Index<usize, Output = T> {
    fn len(&self) -> usize;
    fn iter(&self) -> Box<Iterator<Item = T>>;
}
<|endoftext|>


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

// spell-checker:ignore (ToDO) ENOTDIR

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::fs;
use std::path::Path;

static ABOUT: &str = "Remove the DIRECTORY(ies), if they are empty.";
static OPT_IGNORE_FAIL_NON_EMPTY: &str = "ignore-fail-on-non-empty";
static OPT_PARENTS: &str = "parents";
static OPT_VERBOSE: &str = "verbose";

static ARG_DIRS: &str = "dirs";

#[cfg(unix)]
static ENOTDIR: i32 = 20;
#[cfg(windows)]
static ENOTDIR: i32 = 267;

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

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

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
            Arg::with_name(OPT_IGNORE_FAIL_NON_EMPTY)
                .long(OPT_IGNORE_FAIL_NON_EMPTY)
                .help("ignore each failure that is solely because a directory is non-empty"),
        )
        .arg(
            Arg::with_name(OPT_PARENTS)
                .short("p")
                .long(OPT_PARENTS)
                .help(
                    "remove DIRECTORY and its ancestors; e.g.,
                  'rmdir -p a/b/c' is similar to rmdir a/b/c a/b a",
                ),
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
                .short("v")
                .long(OPT_VERBOSE)
                .help("output a diagnostic for every directory processed"),
        )
        .arg(
            Arg::with_name(ARG_DIRS)
                .multiple(true)
                .takes_value(true)
                .min_values(1)
                .required(true),
        )
        .get_matches_from(args);

    let dirs: Vec<String> = matches
        .values_of(ARG_DIRS)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let ignore = matches.is_present(OPT_IGNORE_FAIL_NON_EMPTY);
    let parents = matches.is_present(OPT_PARENTS);
    let verbose = matches.is_present(OPT_VERBOSE);

    match remove(dirs, ignore, parents, verbose) {
        Ok(()) => ( /* pass */ ),
        Err(e) => return e,
    }

    0
}

fn remove(dirs: Vec<String>, ignore: bool, parents: bool, verbose: bool) -> Result<(), i32> {
    let mut r = Ok(());

    for dir in &dirs {
        let path = Path::new(&dir[..]);
        r = remove_dir(path, ignore, verbose).and(r);
        if parents {
            let mut p = path;
            while let Some(new_p) = p.parent() {
                p = new_p;
                match p.as_os_str().to_str() {
                    None => break,
                    Some(s) => match s {
                        "" | "." | "/" => break,
                        _ => (),
                    },
                };
                r = remove_dir(p, ignore, verbose).and(r);
            }
        }
    }

    r
}

fn remove_dir(path: &Path, ignore: bool, verbose: bool) -> Result<(), i32> {
    let mut read_dir = match fs::read_dir(path) {
        Ok(m) => m,
        Err(e) if e.raw_os_error() == Some(ENOTDIR) => {
            show_error!("failed to remove '{}': Not a directory", path.display());
            return Err(1);
        }
        Err(e) => {
            show_error!("reading directory '{}': {}", path.display(), e);
            return Err(1);
        }
    };

    let mut r = Ok(());

    if read_dir.next().is_none() {
        match fs::remove_dir(path) {
            Err(e) => {
                show_error!("removing directory '{}': {}", path.display(), e);
                r = Err(1);
            }
            Ok(_) if verbose => println!("removing directory, '{}'", path.display()),
            _ => (),
        }
    } else if !ignore {
        show_error!("failed to remove '{}': Directory not empty", path.display());
        r = Err(1);
    }

    r
}
<commit_msg>rmdir: clean up returning Err<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Alex Lyon <arcterus@mail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) ENOTDIR

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use std::fs;
use std::path::Path;

static ABOUT: &str = "Remove the DIRECTORY(ies), if they are empty.";
static OPT_IGNORE_FAIL_NON_EMPTY: &str = "ignore-fail-on-non-empty";
static OPT_PARENTS: &str = "parents";
static OPT_VERBOSE: &str = "verbose";

static ARG_DIRS: &str = "dirs";

#[cfg(unix)]
static ENOTDIR: i32 = 20;
#[cfg(windows)]
static ENOTDIR: i32 = 267;

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

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

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .arg(
            Arg::with_name(OPT_IGNORE_FAIL_NON_EMPTY)
                .long(OPT_IGNORE_FAIL_NON_EMPTY)
                .help("ignore each failure that is solely because a directory is non-empty"),
        )
        .arg(
            Arg::with_name(OPT_PARENTS)
                .short("p")
                .long(OPT_PARENTS)
                .help(
                    "remove DIRECTORY and its ancestors; e.g.,
                  'rmdir -p a/b/c' is similar to rmdir a/b/c a/b a",
                ),
        )
        .arg(
            Arg::with_name(OPT_VERBOSE)
                .short("v")
                .long(OPT_VERBOSE)
                .help("output a diagnostic for every directory processed"),
        )
        .arg(
            Arg::with_name(ARG_DIRS)
                .multiple(true)
                .takes_value(true)
                .min_values(1)
                .required(true),
        )
        .get_matches_from(args);

    let dirs: Vec<String> = matches
        .values_of(ARG_DIRS)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let ignore = matches.is_present(OPT_IGNORE_FAIL_NON_EMPTY);
    let parents = matches.is_present(OPT_PARENTS);
    let verbose = matches.is_present(OPT_VERBOSE);

    match remove(dirs, ignore, parents, verbose) {
        Ok(()) => ( /* pass */ ),
        Err(e) => return e,
    }

    0
}

fn remove(dirs: Vec<String>, ignore: bool, parents: bool, verbose: bool) -> Result<(), i32> {
    let mut r = Ok(());

    for dir in &dirs {
        let path = Path::new(&dir[..]);
        r = remove_dir(path, ignore, verbose).and(r);
        if parents {
            let mut p = path;
            while let Some(new_p) = p.parent() {
                p = new_p;
                match p.as_os_str().to_str() {
                    None => break,
                    Some(s) => match s {
                        "" | "." | "/" => break,
                        _ => (),
                    },
                };
                r = remove_dir(p, ignore, verbose).and(r);
            }
        }
    }

    r
}

fn remove_dir(path: &Path, ignore: bool, verbose: bool) -> Result<(), i32> {
    let mut read_dir = fs::read_dir(path).map_err(|e| {
        if e.raw_os_error() == Some(ENOTDIR) {
            show_error!("failed to remove '{}': Not a directory", path.display());
        } else {
            show_error!("reading directory '{}': {}", path.display(), e);
        }
        1
    })?;

    let mut r = Ok(());

    if read_dir.next().is_none() {
        match fs::remove_dir(path) {
            Err(e) => {
                show_error!("removing directory '{}': {}", path.display(), e);
                r = Err(1);
            }
            Ok(_) if verbose => println!("removing directory, '{}'", path.display()),
            _ => (),
        }
    } else if !ignore {
        show_error!("failed to remove '{}': Directory not empty", path.display());
        r = Err(1);
    }

    r
}
<|endoftext|>