<commit_before>use reqwest;
use serde::Serialize;
use serde_json::{self, Value};
use serde_json::error::Error as JsonError;

use std::thread;
use std::sync::mpsc::{self, channel};
use std::fmt;
use std::error::Error;
use std::time::Duration;

use websocket::{Message, OwnedMessage};
use websocket::client::ClientBuilder;
use websocket::result::WebSocketError;

#[derive(Debug)]
pub enum ConnectionError {
    FailedToFetchUrl( reqwest::Error ),
    WebSocketError( WebSocketError )
}

impl Error for ConnectionError {
    fn description( &self ) -> &str {
        match *self {
            ConnectionError::FailedToFetchUrl( _ ) => "failed to fetch websocket debugger URL",
            ConnectionError::WebSocketError( _ ) => "web socket error"
        }
    }
}

impl fmt::Display for ConnectionError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            ConnectionError::FailedToFetchUrl( ref message ) => write!( fmt, "{}: {}", self.description(), message ),
            ConnectionError::WebSocketError( ref message ) => write!( fmt, "{}: {}", self.description(), message )
        }
    }
}

impl From< reqwest::Error > for ConnectionError {
    fn from( error: reqwest::Error ) -> Self {
        ConnectionError::FailedToFetchUrl( error )
    }
}

impl From< WebSocketError > for ConnectionError {
    fn from( error: WebSocketError ) -> Self {
        ConnectionError::WebSocketError( error )
    }
}

#[derive(Debug)]
pub enum CommunicationError {
    Send( WebSocketError ),
    Recv( WebSocketError )
}

impl Error for CommunicationError {
    fn description( &self ) -> &str {
        match *self {
            CommunicationError::Send( _ ) => "error while sending web socket message",
            CommunicationError::Recv( _ ) => "error while receiving web socket message"
        }
    }
}

impl fmt::Display for CommunicationError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            CommunicationError::Send( ref message ) => write!( fmt, "{}: {}", self.description(), message ),
            CommunicationError::Recv( ref message ) => write!( fmt, "{}: {}", self.description(), message )
        }
    }
}

#[derive(Debug)]
pub enum ReplyError {
    Timeout,
    MalformedMessage( &'static str ),
    MalformedJson( JsonError ),
    CommunicationError( CommunicationError )
}

impl Error for ReplyError {
    fn description( &self ) -> &str {
        match *self {
            ReplyError::Timeout => "timeout while waiting for reply",
            ReplyError::MalformedMessage( _ ) => "received malformed message",
            ReplyError::MalformedJson( _ ) => "received malformed JSON",
            ReplyError::CommunicationError( _ ) => "communication error"
        }
    }
}

impl fmt::Display for ReplyError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            ReplyError::Timeout => write!( fmt, "{}", self.description() ),
            ReplyError::MalformedMessage( ref error ) => write!( fmt, "{}: {}", self.description(), error ),
            ReplyError::MalformedJson( ref error ) => write!( fmt, "{}: {}", self.description(), error ),
            ReplyError::CommunicationError( ref error ) => write!( fmt, "{}: {}", self.description(), error )
        }
    }
}

impl From< CommunicationError > for ReplyError {
    fn from( error: CommunicationError ) -> Self {
        ReplyError::CommunicationError( error )
    }
}

impl From< JsonError > for ReplyError {
    fn from( error: JsonError ) -> Self {
        ReplyError::MalformedJson( error )
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct RequestId( u64 );

#[derive(Clone, Debug)]
pub enum Reply {
    Event {
        method: String,
        body: Value
    },
    Result {
        id: RequestId,
        body: Value
    },
    Error {
        id: RequestId,
        code: i64,
        message: String
    }
}

pub struct Connection {
    tx: mpsc::Sender< OwnedMessage >,
    rx: mpsc::Receiver< Result< OwnedMessage, CommunicationError > >,
    last_id: u64
}

fn owned_message_to_json( message: Result< OwnedMessage, CommunicationError > ) -> Result< Value, ReplyError > {
    match message? {
        OwnedMessage::Text( text ) => {
            match serde_json::from_str( &text ) {
                Ok( value ) => return Ok( value ),
                Err( error ) => return Err( error.into() )
            }
        },
        _ => return Err( ReplyError::MalformedMessage( "non text message received" ) )
    }
}

impl Connection {
    pub fn connect( json_url: &str ) -> Result< Self , ConnectionError > {
        let mut response = reqwest::get( json_url )?;
        let text = response.text()?;
        let json: Value = serde_json::from_str( &text ).unwrap();

        debug!( "Got initial response: {:?}", json );

        let url = json.get( 0 ).expect( "can't find first element" )
            .get( "webSocketDebuggerUrl" ).expect( "no webSocketDebuggerUrl found" )
            .as_str().expect( "webSocketDebuggerUrl is not a string" );

        debug!( "Got websocket debugger URL {}", url );

        let client = ClientBuilder::new( &url ).unwrap().connect_insecure()?;
        debug!( "Connected to: {}", url );

        let (mut receiver, mut sender) = client.split().unwrap();

        let (output_tx, output_rx) = channel();
        let (input_tx, input_rx) = channel();

        let input_tx_clone = input_tx.clone();
        thread::spawn( move || {
            loop {
                let message = match output_rx.recv() {
                    Ok( message ) => message,
                    Err( _ ) => break
                };

                debug!( "Sending: {:?}", message );
                if let Err( error ) = sender.send_message( &message ) {
                    let _ = input_tx_clone.send( Err( CommunicationError::Send( error ) ) );
                    let _ = sender.send_message( &Message::close() );
                    break;
                }

                if let OwnedMessage::Close( _ ) = message {
                    break;
                }
            }
        });

        let output_tx_clone = output_tx.clone();
        thread::spawn( move || {
            for message in receiver.incoming_messages() {
                trace!( "Received: {:?}", message );
                let message = match message {
                    Ok( message ) => message,
                    Err( error ) => {
                        let _ = input_tx.send( Err( CommunicationError::Recv( error ) ) );
                        let _ = output_tx_clone.send( OwnedMessage::Close( None ) );
                        break;
                    }
                };

                match message {
                    message @ OwnedMessage::Close( _ ) => {
                        let _ = output_tx_clone.send( message.clone() );
                        let _ = input_tx.send( Ok( message ) );
                        break;
                    }
                    message @ OwnedMessage::Ping( _ ) => {
                        match output_tx_clone.send( message ) {
                            Ok(()) => {},
                            Err( _ ) => break
                        }
                    }
                    message => {
                        match input_tx.send( Ok( message ) ) {
                            Ok(()) => {},
                            Err( _ ) => break
                        }
                    },
                }
            }
        });

        Ok( Connection {
            tx: output_tx,
            rx: input_rx,
            last_id: 0
        })
    }

    fn raw_send( &mut self, message: OwnedMessage ) {
        let _ = self.tx.send( message );
    }

    pub fn send_cmd< T: Serialize >( &mut self, method: &str, params: T ) -> RequestId {
        #[derive(Serialize)]
        struct Command< T: Serialize > {
            method: String,
            params: T,
            id: u64
        }

        let id = self.last_id;
        let command = Command {
            method: method.to_owned(),
            params,
            id
        };

        self.last_id += 1;
        let message = serde_json::to_string( &command ).unwrap();
        self.raw_send( OwnedMessage::Text( message ) );

        RequestId( id )
    }

    fn raw_try_recv( &mut self, wait_for: Option< Duration > ) -> Option< Result< OwnedMessage, CommunicationError > > {
        if let Some( wait_for ) = wait_for {
            match self.rx.recv_timeout( wait_for ) {
                Ok( message ) => Some( message ),
                Err( _ ) => None
            }
        } else {
            match self.rx.try_recv() {
                Ok( message ) => Some( message ),
                Err( _ ) => None
            }
        }
    }

    fn json_try_recv( &mut self, wait_for: Option< Duration > ) -> Result< Value, ReplyError > {
        let message = self.raw_try_recv( wait_for );
        match message {
            Some( message ) => return owned_message_to_json( message ),
            None => return Err( ReplyError::Timeout )
        }
    }

    pub fn try_recv( &mut self, wait_for: Option< Duration > ) -> Result< Reply, ReplyError > {
        let json = self.json_try_recv( wait_for )?;
        if let Some( error ) = json.get( "error" ) {
            let id = json.get( "id" )
                .ok_or( ReplyError::MalformedMessage( "'id' field not found" ) )?
                .as_u64()
                .ok_or( ReplyError::MalformedMessage( "'id' field is not an integer" ) )?;
            let id = RequestId( id );
            let code = error.get( "code" )
                .ok_or( ReplyError::MalformedMessage( "'error.code' field not found" ) )?
                .as_i64()
                .ok_or( ReplyError::MalformedMessage( "'error.code' field is not an integer" ) )?;
            let message = error.get( "message" )
                .ok_or( ReplyError::MalformedMessage( "'error.message' field not found" ) )?
                .as_str()
                .ok_or( ReplyError::MalformedMessage( "'error.message' field is not an integer" ) )?
                .to_owned();
            Ok( Reply::Error { id, code, message } )
        } else if let Some( id ) = json.get( "id" ) {
            let id = id.as_u64().ok_or( ReplyError::MalformedMessage( "'id' field is not an integer" ) )?;
            let id = RequestId( id );
            let body = json.get( "result" ).ok_or( ReplyError::MalformedMessage( "'body' field not found" ) )?.clone();
            Ok( Reply::Result { id, body } )
        } else {
            let method = json.get( "method" )
                .ok_or( ReplyError::MalformedMessage( "'method' field not found" ) )?
                .as_str()
                .ok_or( ReplyError::MalformedMessage( "'method' field is not a string" ) )?
                .to_owned();
            let body = json.get( "params" ).ok_or( ReplyError::MalformedMessage( "'params' field not found" ) )?.clone();
            Ok( Reply::Event { method, body } )
        }
    }
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject
#[derive(Clone, Deserialize, Debug)]
pub struct RemoteObject {
    #[serde(rename = "type")]
    pub kind: String,
    pub value: Option< Value >,
    #[serde(rename = "className")]
    pub class_name: Option< String >,
    pub description: Option< String >
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-consoleAPICalled
#[derive(Clone, Deserialize, Debug)]
pub struct ConsoleApiCalledBody {
    #[serde(rename = "type")]
    pub kind: String,
    pub args: Vec< RemoteObject >
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionThrown
#[derive(Clone, Deserialize, Debug)]
pub struct ExceptionThrownBody {
    #[serde(rename = "exceptionDetails")]
    pub exception_details: ExceptionDetails
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExceptionDetails
#[derive(Clone, Deserialize, Debug)]
pub struct ExceptionDetails {
    pub text: String,
    pub exception: Option< RemoteObject >,
    #[serde(rename = "lineNumber")]
    pub line_number: u32,
    #[serde(rename = "columnNumber")]
    pub column_number: u32,
    #[serde(rename = "scriptId")]
    pub script_id: Option< String >,
    pub url: Option< String >
}
<commit_msg>Add a workaround for Chromium sometimes not initializing in time<commit_after>use reqwest;
use serde::Serialize;
use serde_json::{self, Value};
use serde_json::error::Error as JsonError;

use std::thread;
use std::sync::mpsc::{self, channel};
use std::fmt;
use std::error::Error;
use std::time::{Duration, Instant};

use websocket::{Message, OwnedMessage};
use websocket::client::ClientBuilder;
use websocket::result::WebSocketError;

#[derive(Debug)]
pub enum ConnectionError {
    FailedToFetchUrl( reqwest::Error ),
    WebSocketError( WebSocketError )
}

impl Error for ConnectionError {
    fn description( &self ) -> &str {
        match *self {
            ConnectionError::FailedToFetchUrl( _ ) => "failed to fetch websocket debugger URL",
            ConnectionError::WebSocketError( _ ) => "web socket error"
        }
    }
}

impl fmt::Display for ConnectionError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            ConnectionError::FailedToFetchUrl( ref message ) => write!( fmt, "{}: {}", self.description(), message ),
            ConnectionError::WebSocketError( ref message ) => write!( fmt, "{}: {}", self.description(), message )
        }
    }
}

impl From< reqwest::Error > for ConnectionError {
    fn from( error: reqwest::Error ) -> Self {
        ConnectionError::FailedToFetchUrl( error )
    }
}

impl From< WebSocketError > for ConnectionError {
    fn from( error: WebSocketError ) -> Self {
        ConnectionError::WebSocketError( error )
    }
}

#[derive(Debug)]
pub enum CommunicationError {
    Send( WebSocketError ),
    Recv( WebSocketError )
}

impl Error for CommunicationError {
    fn description( &self ) -> &str {
        match *self {
            CommunicationError::Send( _ ) => "error while sending web socket message",
            CommunicationError::Recv( _ ) => "error while receiving web socket message"
        }
    }
}

impl fmt::Display for CommunicationError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            CommunicationError::Send( ref message ) => write!( fmt, "{}: {}", self.description(), message ),
            CommunicationError::Recv( ref message ) => write!( fmt, "{}: {}", self.description(), message )
        }
    }
}

#[derive(Debug)]
pub enum ReplyError {
    Timeout,
    MalformedMessage( &'static str ),
    MalformedJson( JsonError ),
    CommunicationError( CommunicationError )
}

impl Error for ReplyError {
    fn description( &self ) -> &str {
        match *self {
            ReplyError::Timeout => "timeout while waiting for reply",
            ReplyError::MalformedMessage( _ ) => "received malformed message",
            ReplyError::MalformedJson( _ ) => "received malformed JSON",
            ReplyError::CommunicationError( _ ) => "communication error"
        }
    }
}

impl fmt::Display for ReplyError {
    fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result {
        match *self {
            ReplyError::Timeout => write!( fmt, "{}", self.description() ),
            ReplyError::MalformedMessage( ref error ) => write!( fmt, "{}: {}", self.description(), error ),
            ReplyError::MalformedJson( ref error ) => write!( fmt, "{}: {}", self.description(), error ),
            ReplyError::CommunicationError( ref error ) => write!( fmt, "{}: {}", self.description(), error )
        }
    }
}

impl From< CommunicationError > for ReplyError {
    fn from( error: CommunicationError ) -> Self {
        ReplyError::CommunicationError( error )
    }
}

impl From< JsonError > for ReplyError {
    fn from( error: JsonError ) -> Self {
        ReplyError::MalformedJson( error )
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct RequestId( u64 );

#[derive(Clone, Debug)]
pub enum Reply {
    Event {
        method: String,
        body: Value
    },
    Result {
        id: RequestId,
        body: Value
    },
    Error {
        id: RequestId,
        code: i64,
        message: String
    }
}

pub struct Connection {
    tx: mpsc::Sender< OwnedMessage >,
    rx: mpsc::Receiver< Result< OwnedMessage, CommunicationError > >,
    last_id: u64
}

fn owned_message_to_json( message: Result< OwnedMessage, CommunicationError > ) -> Result< Value, ReplyError > {
    match message? {
        OwnedMessage::Text( text ) => {
            match serde_json::from_str( &text ) {
                Ok( value ) => return Ok( value ),
                Err( error ) => return Err( error.into() )
            }
        },
        _ => return Err( ReplyError::MalformedMessage( "non text message received" ) )
    }
}

impl Connection {
    pub fn connect( json_url: &str ) -> Result< Self , ConnectionError > {
        let start = Instant::now();
        let mut initial_json = None;
        while start.elapsed() < Duration::from_secs( 3 ) {
            let mut response = reqwest::get( json_url )?;
            let text = response.text()?;
            let json: Value = serde_json::from_str( &text ).unwrap();

            debug!( "Got initial response: {:?}", json );

            // This tends to be race-y and very rarely fails,
            // so we'll retry a few times.
            if json.get( 0 ).is_none() {
                thread::sleep( Duration::from_millis( 100 ) );
                continue;
            } else {
                initial_json = Some( json );
                break;
            }
        }

        let json = initial_json.expect( "timed out while waiting for Chromium to give us websocket debugger URL" );
        let url = json.get( 0 ).expect( "can't find first element" )
            .get( "webSocketDebuggerUrl" ).expect( "no webSocketDebuggerUrl found" )
            .as_str().expect( "webSocketDebuggerUrl is not a string" );

        debug!( "Got websocket debugger URL {}", url );

        let client = ClientBuilder::new( &url ).unwrap().connect_insecure()?;
        debug!( "Connected to: {}", url );

        let (mut receiver, mut sender) = client.split().unwrap();

        let (output_tx, output_rx) = channel();
        let (input_tx, input_rx) = channel();

        let input_tx_clone = input_tx.clone();
        thread::spawn( move || {
            loop {
                let message = match output_rx.recv() {
                    Ok( message ) => message,
                    Err( _ ) => break
                };

                debug!( "Sending: {:?}", message );
                if let Err( error ) = sender.send_message( &message ) {
                    let _ = input_tx_clone.send( Err( CommunicationError::Send( error ) ) );
                    let _ = sender.send_message( &Message::close() );
                    break;
                }

                if let OwnedMessage::Close( _ ) = message {
                    break;
                }
            }
        });

        let output_tx_clone = output_tx.clone();
        thread::spawn( move || {
            for message in receiver.incoming_messages() {
                trace!( "Received: {:?}", message );
                let message = match message {
                    Ok( message ) => message,
                    Err( error ) => {
                        let _ = input_tx.send( Err( CommunicationError::Recv( error ) ) );
                        let _ = output_tx_clone.send( OwnedMessage::Close( None ) );
                        break;
                    }
                };

                match message {
                    message @ OwnedMessage::Close( _ ) => {
                        let _ = output_tx_clone.send( message.clone() );
                        let _ = input_tx.send( Ok( message ) );
                        break;
                    }
                    message @ OwnedMessage::Ping( _ ) => {
                        match output_tx_clone.send( message ) {
                            Ok(()) => {},
                            Err( _ ) => break
                        }
                    }
                    message => {
                        match input_tx.send( Ok( message ) ) {
                            Ok(()) => {},
                            Err( _ ) => break
                        }
                    },
                }
            }
        });

        Ok( Connection {
            tx: output_tx,
            rx: input_rx,
            last_id: 0
        })
    }

    fn raw_send( &mut self, message: OwnedMessage ) {
        let _ = self.tx.send( message );
    }

    pub fn send_cmd< T: Serialize >( &mut self, method: &str, params: T ) -> RequestId {
        #[derive(Serialize)]
        struct Command< T: Serialize > {
            method: String,
            params: T,
            id: u64
        }

        let id = self.last_id;
        let command = Command {
            method: method.to_owned(),
            params,
            id
        };

        self.last_id += 1;
        let message = serde_json::to_string( &command ).unwrap();
        self.raw_send( OwnedMessage::Text( message ) );

        RequestId( id )
    }

    fn raw_try_recv( &mut self, wait_for: Option< Duration > ) -> Option< Result< OwnedMessage, CommunicationError > > {
        if let Some( wait_for ) = wait_for {
            match self.rx.recv_timeout( wait_for ) {
                Ok( message ) => Some( message ),
                Err( _ ) => None
            }
        } else {
            match self.rx.try_recv() {
                Ok( message ) => Some( message ),
                Err( _ ) => None
            }
        }
    }

    fn json_try_recv( &mut self, wait_for: Option< Duration > ) -> Result< Value, ReplyError > {
        let message = self.raw_try_recv( wait_for );
        match message {
            Some( message ) => return owned_message_to_json( message ),
            None => return Err( ReplyError::Timeout )
        }
    }

    pub fn try_recv( &mut self, wait_for: Option< Duration > ) -> Result< Reply, ReplyError > {
        let json = self.json_try_recv( wait_for )?;
        if let Some( error ) = json.get( "error" ) {
            let id = json.get( "id" )
                .ok_or( ReplyError::MalformedMessage( "'id' field not found" ) )?
                .as_u64()
                .ok_or( ReplyError::MalformedMessage( "'id' field is not an integer" ) )?;
            let id = RequestId( id );
            let code = error.get( "code" )
                .ok_or( ReplyError::MalformedMessage( "'error.code' field not found" ) )?
                .as_i64()
                .ok_or( ReplyError::MalformedMessage( "'error.code' field is not an integer" ) )?;
            let message = error.get( "message" )
                .ok_or( ReplyError::MalformedMessage( "'error.message' field not found" ) )?
                .as_str()
                .ok_or( ReplyError::MalformedMessage( "'error.message' field is not an integer" ) )?
                .to_owned();
            Ok( Reply::Error { id, code, message } )
        } else if let Some( id ) = json.get( "id" ) {
            let id = id.as_u64().ok_or( ReplyError::MalformedMessage( "'id' field is not an integer" ) )?;
            let id = RequestId( id );
            let body = json.get( "result" ).ok_or( ReplyError::MalformedMessage( "'body' field not found" ) )?.clone();
            Ok( Reply::Result { id, body } )
        } else {
            let method = json.get( "method" )
                .ok_or( ReplyError::MalformedMessage( "'method' field not found" ) )?
                .as_str()
                .ok_or( ReplyError::MalformedMessage( "'method' field is not a string" ) )?
                .to_owned();
            let body = json.get( "params" ).ok_or( ReplyError::MalformedMessage( "'params' field not found" ) )?.clone();
            Ok( Reply::Event { method, body } )
        }
    }
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject
#[derive(Clone, Deserialize, Debug)]
pub struct RemoteObject {
    #[serde(rename = "type")]
    pub kind: String,
    pub value: Option< Value >,
    #[serde(rename = "className")]
    pub class_name: Option< String >,
    pub description: Option< String >
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-consoleAPICalled
#[derive(Clone, Deserialize, Debug)]
pub struct ConsoleApiCalledBody {
    #[serde(rename = "type")]
    pub kind: String,
    pub args: Vec< RemoteObject >
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionThrown
#[derive(Clone, Deserialize, Debug)]
pub struct ExceptionThrownBody {
    #[serde(rename = "exceptionDetails")]
    pub exception_details: ExceptionDetails
}

// https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExceptionDetails
#[derive(Clone, Deserialize, Debug)]
pub struct ExceptionDetails {
    pub text: String,
    pub exception: Option< RemoteObject >,
    #[serde(rename = "lineNumber")]
    pub line_number: u32,
    #[serde(rename = "columnNumber")]
    pub column_number: u32,
    #[serde(rename = "scriptId")]
    pub script_id: Option< String >,
    pub url: Option< String >
}
<|endoftext|>


<commit_before>#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    storage: Storage,
    align: [Align; 0],
}

impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    #[inline]
    pub fn new(storage: Storage) -> Self {
        Self {
            storage,
            align: [],
        }
    }

    #[inline]
    pub fn get_bit(&self, index: usize) -> bool {
        debug_assert!(index / 8 < self.storage.as_ref().len());

        let byte_index = index / 8;
        let byte = self.storage.as_ref()[byte_index];

        let bit_index = index % 8;
        let mask = 1 << bit_index;

        byte & mask == mask
    }

    #[inline]
    pub fn set_bit(&mut self, index: usize, val: bool) {
        debug_assert!(index / 8 < self.storage.as_ref().len());

        let byte_index = index / 8;
        let byte = &mut self.storage.as_mut()[byte_index];

        let bit_index = index % 8;
        let mask = 1 << bit_index;

        if val {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    #[inline]
    pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

        let mut val = 0;

        for i in 0..(bit_width as usize) {
            if self.get_bit(i + bit_offset) {
                val |= 1 << i;
            }
        }

        val
    }

    #[inline]
    pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

        for i in 0..(bit_width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            self.set_bit(i + bit_offset, val_bit_is_set);
        }
    }
}
<commit_msg>Fix bitfields on big-endian machines.<commit_after>#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    storage: Storage,
    align: [Align; 0],
}

impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    #[inline]
    pub fn new(storage: Storage) -> Self {
        Self {
            storage,
            align: [],
        }
    }

    #[inline]
    pub fn get_bit(&self, index: usize) -> bool {
        debug_assert!(index / 8 < self.storage.as_ref().len());

        let byte_index = index / 8;
        let byte = self.storage.as_ref()[byte_index];

        let mut bit_index = index % 8;
        #[cfg(target_endian = "big")]
        {
            // Adjust the index for endianness.
            bit_index = 7 - bit_index;
        }
        let mask = 1 << bit_index;

        byte & mask == mask
    }

    #[inline]
    pub fn set_bit(&mut self, index: usize, val: bool) {
        debug_assert!(index / 8 < self.storage.as_ref().len());

        let byte_index = index / 8;
        let byte = &mut self.storage.as_mut()[byte_index];

        let mut bit_index = index % 8;
        #[cfg(target_endian = "big")]
        {
            // Adjust the index for endianness.
            bit_index = 7 - bit_index;
        }
        let mask = 1 << bit_index;

        if val {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }

    #[inline]
    pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

        let mut val = 0;

        for i in 0..(bit_width as usize) {
            if self.get_bit(i + bit_offset) {
                let mut index  = i;
                #[cfg(target_endian = "big")]
                {
                   // Adjust the index for endianness.
                   index  = bit_width as usize - 1 - index;
                }
                val |= 1 << index;
            }
        }

        val
    }

    #[inline]
    pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());

        for i in 0..(bit_width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            let mut index  = i;
            #[cfg(target_endian = "big")]
            {
                // Adjust the index for endianness.
                index  = bit_width as usize - 1 - index;
            }
            self.set_bit(index + bit_offset, val_bit_is_set);
        }
    }
}
<|endoftext|>


<commit_before>use rustc::hir;
use rustc::mir::ProjectionElem;
use rustc::mir::{Mir, Place, PlaceBase, Mutability, Static, StaticKind};
use rustc::ty::{self, TyCtxt};
use crate::borrow_check::borrow_set::LocalsStateAtExit;

/// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> {
    /// Returns `true` if we can safely ignore borrows of this place.
    /// This is true whenever there is no action that the user can do
    /// to the place `self` that would invalidate the borrow. This is true
    /// for borrows of raw pointer dereferents as well as shared references.
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
        ) -> bool;
}

impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
    ) -> bool {
        match self {
            // If a local variable is immutable, then we only need to track borrows to guard
            // against two kinds of errors:
            // * The variable being dropped while still borrowed (e.g., because the fn returns
            //   a reference to a local variable)
            // * The variable being moved while still borrowed
            //
            // In particular, the variable cannot be mutated -- the "access checks" will fail --
            // so we don't have to worry about mutation while borrowed.
            Place::Base(PlaceBase::Local(index)) => {
                match locals_state_at_exit {
                    LocalsStateAtExit::AllAreInvalidated => false,
                    LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
                        let ignore = !has_storage_dead_or_moved.contains(*index) &&
                            mir.local_decls[*index].mutability == Mutability::Not;
                        debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
                        ignore
                    }
                }
            }
            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) =>
                false,
            Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => {
                tcx.is_mutable_static(*def_id)
            }
            Place::Projection(proj) => match proj.elem {
                ProjectionElem::Field(..)
                | ProjectionElem::Downcast(..)
                | ProjectionElem::Subslice { .. }
                | ProjectionElem::ConstantIndex { .. }
                | ProjectionElem::Index(_) => proj.base.ignore_borrow(
                    tcx, mir, locals_state_at_exit),

                ProjectionElem::Deref => {
                    let ty = proj.base.ty(mir, tcx).ty;
                    match ty.sty {
                        // For both derefs of raw pointers and `&T`
                        // references, the original path is `Copy` and
                        // therefore not significant.  In particular,
                        // there is nothing the user can do to the
                        // original path that would invalidate the
                        // newly created reference -- and if there
                        // were, then the user could have copied the
                        // original path into a new variable and
                        // borrowed *that* one, leaving the original
                        // path unborrowed.
                        ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true,
                        _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit),
                    }
                }
            },
        }
    }
}
<commit_msg>Make ignore_borrow iterate instead of recurse<commit_after>use rustc::hir;
use rustc::mir::ProjectionElem;
use rustc::mir::{Mir, Place, PlaceBase, Mutability, Static, StaticKind};
use rustc::ty::{self, TyCtxt};
use crate::borrow_check::borrow_set::LocalsStateAtExit;

/// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> {
    /// Returns `true` if we can safely ignore borrows of this place.
    /// This is true whenever there is no action that the user can do
    /// to the place `self` that would invalidate the borrow. This is true
    /// for borrows of raw pointer dereferents as well as shared references.
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
        ) -> bool;
}

impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
    ) -> bool {
        self.iterate(|place_base, place_projection| {
            let ignore = match place_base {
                // If a local variable is immutable, then we only need to track borrows to guard
                // against two kinds of errors:
                // * The variable being dropped while still borrowed (e.g., because the fn returns
                //   a reference to a local variable)
                // * The variable being moved while still borrowed
                //
                // In particular, the variable cannot be mutated -- the "access checks" will fail --
                // so we don't have to worry about mutation while borrowed.
                PlaceBase::Local(index) => {
                    match locals_state_at_exit {
                        LocalsStateAtExit::AllAreInvalidated => false,
                        LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
                            let ignore = !has_storage_dead_or_moved.contains(*index) &&
                                mir.local_decls[*index].mutability == Mutability::Not;
                            debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
                            ignore
                        }
                    }
                }
                PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) =>
                    false,
                PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
                    tcx.is_mutable_static(*def_id)
                }
            };

            for proj in place_projection {
                if proj.elem == ProjectionElem::Deref {
                    let ty = proj.base.ty(mir, tcx).ty;
                    match ty.sty {
                        // For both derefs of raw pointers and `&T`
                        // references, the original path is `Copy` and
                        // therefore not significant.  In particular,
                        // there is nothing the user can do to the
                        // original path that would invalidate the
                        // newly created reference -- and if there
                        // were, then the user could have copied the
                        // original path into a new variable and
                        // borrowed *that* one, leaving the original
                        // path unborrowed.
                        ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => return true,
                        _ => {}
                    }
                }
            }

            ignore
        })
    }
}
<|endoftext|>


<commit_before>mod ui;

use crate::config::Config;
use crate::err::{self, Result};
use crate::file::SaveDir;
use crate::track::SeriesTracker;
use crate::util;
use anime::remote::RemoteService;
use anime::Series;
use chrono::{DateTime, Duration, Utc};
use clap::ArgMatches;
use snafu::{ensure, OptionExt, ResultExt};
use std::borrow::Cow;
use std::ops::Add;
use std::process;
use termion::event::Key;
use ui::{Event, Events, UI};

pub fn run(args: &ArgMatches) -> Result<()> {
    let name = crate::get_series_name(args)?;
    let config = crate::get_config()?;

    let remote = crate::get_remote(args, true)?;
    let remote = remote.as_ref();

    let episodes = crate::get_episodes(args, &name, &config)?;
    let seasons = crate::get_season_list(&name, remote, &episodes)?;

    let mut state = {
        let series_names = SaveDir::LocalData.get_subdirs()?;
        let selected_series = series_names.iter().position(|s| *s == name).unwrap_or(0);
        let season_num = crate::get_season_num(args);

        let series = Series::from_season_list(&seasons, season_num, &episodes)?;
        let season_state = SeasonState::new(remote, &name, series, season_num)?;

        SeriesState {
            season: season_state,
            series_names,
            selected_series,
            num_seasons: seasons.len(),
        }
    };

    let mut ui = UI::init()?;
    let events = Events::new(Duration::seconds(1));

    loop {
        ui.draw(&state)?;

        match events.next()? {
            Event::Input(key) => match key {
                // Exit
                Key::Char('q') => {
                    // Prevent ruining the user's terminal
                    ui.clear().ok();
                    break Ok(());
                }
                // Play next episode
                Key::Char('\n') => state.season.play_next_episode_async(remote, &config)?,
                // Select season
                Key::Up | Key::Down if state.season.watch_state == WatchState::Idle => {
                    let season = &mut state.season;

                    let next_season = if key == Key::Up {
                        season.season_num.saturating_sub(1)
                    } else {
                        season.season_num + 1
                    };

                    if seasons.has(next_season) {
                        let series = Series::from_season_list(&seasons, next_season, &episodes)?;
                        *season = SeasonState::new(remote, &name, series, next_season)?;
                    }
                }
                _ => (),
            },
            Event::Tick => match &mut state.season.watch_state {
                WatchState::Idle => (),
                WatchState::Watching(start_time, _, child) => {
                    let status = match child.try_wait().context(err::IO)? {
                        Some(status) => status,
                        None => continue,
                    };

                    ensure!(status.success(), err::AbnormalPlayerExit);

                    let mins_watched = {
                        let watch_time = Utc::now() - *start_time;
                        watch_time.num_seconds() as f32 / 60.0
                    };

                    let season = &mut state.season;

                    let mins_must_watch =
                        season.series.info.episode_length as f32 * config.episode.pcnt_must_watch;

                    if mins_watched >= mins_must_watch {
                        season.tracker.episode_completed(remote, &config)?;
                    }

                    season.watch_state = WatchState::Idle;
                }
            },
        }
    }
}

pub struct SeriesState<'a> {
    pub season: SeasonState<'a>,
    pub series_names: Vec<String>,
    pub selected_series: usize,
    pub num_seasons: usize,
}

pub type ProgressTime = DateTime<Utc>;
pub type StartTime = DateTime<Utc>;

pub enum WatchState {
    Idle,
    Watching(StartTime, ProgressTime, process::Child),
}

impl PartialEq for WatchState {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (WatchState::Idle, WatchState::Idle) => true,
            (WatchState::Watching(_, _, _), WatchState::Watching(_, _, _)) => true,
            _ => false,
        }
    }
}

pub struct SeasonState<'a> {
    pub series: Series<'a>,
    pub tracker: SeriesTracker<'a>,
    pub season_num: usize,
    pub watch_time: String,
    pub score: Cow<'a, str>,
    pub watch_state: WatchState,
}

impl<'a> SeasonState<'a> {
    fn new<R, S>(
        remote: &R,
        name: S,
        series: Series<'a>,
        season_num: usize,
    ) -> Result<SeasonState<'a>>
    where
        R: RemoteService + ?Sized,
        S: Into<String>,
    {
        let watch_time = {
            let watch_time_mins = series.info.episodes * series.info.episode_length;
            util::hm_from_mins(watch_time_mins as f32)
        };

        let tracker = SeriesTracker::init(series.info.clone(), name)?;

        let score = match tracker.state.score() {
            Some(score) => remote.score_to_str(score).into_owned().into(),
            None => "??".into(),
        };

        Ok(SeasonState {
            series,
            tracker,
            season_num,
            watch_time,
            score,
            watch_state: WatchState::Idle,
        })
    }

    fn play_next_episode_async<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        self.tracker.begin_watching(remote, config)?;
        let next_ep = self.tracker.state.watched_eps() + 1;

        let episode = self
            .series
            .get_episode(next_ep)
            .context(err::EpisodeNotFound { episode: next_ep })?;

        let start_time = Utc::now();

        let child = crate::process::open_with_default(episode)
            .context(err::FailedToPlayEpisode { episode: next_ep })?;

        let progress_time = {
            let secs_must_watch =
                (self.series.info.episode_length as f32 * config.episode.pcnt_must_watch) * 60.0;
            let time_must_watch = Duration::seconds(secs_must_watch as i64);

            start_time.add(time_must_watch)
        };

        self.watch_state = WatchState::Watching(start_time, progress_time, child);

        Ok(())
    }
}
<commit_msg>[anup] Add force syncing from/to the remote for the TUI<commit_after>mod ui;

use crate::config::Config;
use crate::err::{self, Result};
use crate::file::SaveDir;
use crate::track::SeriesTracker;
use crate::util;
use anime::remote::RemoteService;
use anime::Series;
use chrono::{DateTime, Duration, Utc};
use clap::ArgMatches;
use snafu::{ensure, OptionExt, ResultExt};
use std::borrow::Cow;
use std::ops::Add;
use std::process;
use termion::event::Key;
use ui::{Event, Events, UI};

pub fn run(args: &ArgMatches) -> Result<()> {
    let name = crate::get_series_name(args)?;
    let config = crate::get_config()?;

    let remote = crate::get_remote(args, true)?;
    let remote = remote.as_ref();

    let episodes = crate::get_episodes(args, &name, &config)?;
    let seasons = crate::get_season_list(&name, remote, &episodes)?;

    let mut state = {
        let series_names = SaveDir::LocalData.get_subdirs()?;
        let selected_series = series_names.iter().position(|s| *s == name).unwrap_or(0);
        let season_num = crate::get_season_num(args);

        let series = Series::from_season_list(&seasons, season_num, &episodes)?;
        let season_state = SeasonState::new(remote, &name, series, season_num)?;

        SeriesState {
            season: season_state,
            series_names,
            selected_series,
            num_seasons: seasons.len(),
        }
    };

    let mut ui = UI::init()?;
    let events = Events::new(Duration::seconds(1));

    loop {
        ui.draw(&state)?;

        match events.next()? {
            Event::Input(key) => match key {
                // Exit
                Key::Char('q') => {
                    // Prevent ruining the user's terminal
                    ui.clear().ok();
                    break Ok(());
                }
                // Sync list entry from remote
                Key::Char('r') => {
                    let entry = &mut state.season.tracker.state;
                    entry.force_sync_changes_from_remote(remote, &name)?;
                }
                // Sync list entry to remote
                Key::Char('s') => {
                    let entry = &mut state.season.tracker.state;
                    entry.force_sync_changes_to_remote(remote, &name)?;
                }
                // Play next episode
                Key::Char('\n') => state.season.play_next_episode_async(remote, &config)?,
                // Select season
                Key::Up | Key::Down if state.season.watch_state == WatchState::Idle => {
                    let season = &mut state.season;

                    let next_season = if key == Key::Up {
                        season.season_num.saturating_sub(1)
                    } else {
                        season.season_num + 1
                    };

                    if seasons.has(next_season) {
                        let series = Series::from_season_list(&seasons, next_season, &episodes)?;
                        *season = SeasonState::new(remote, &name, series, next_season)?;
                    }
                }
                _ => (),
            },
            Event::Tick => match &mut state.season.watch_state {
                WatchState::Idle => (),
                WatchState::Watching(start_time, _, child) => {
                    let status = match child.try_wait().context(err::IO)? {
                        Some(status) => status,
                        None => continue,
                    };

                    ensure!(status.success(), err::AbnormalPlayerExit);

                    let mins_watched = {
                        let watch_time = Utc::now() - *start_time;
                        watch_time.num_seconds() as f32 / 60.0
                    };

                    let season = &mut state.season;

                    let mins_must_watch =
                        season.series.info.episode_length as f32 * config.episode.pcnt_must_watch;

                    if mins_watched >= mins_must_watch {
                        season.tracker.episode_completed(remote, &config)?;
                    }

                    season.watch_state = WatchState::Idle;
                }
            },
        }
    }
}

pub struct SeriesState<'a> {
    pub season: SeasonState<'a>,
    pub series_names: Vec<String>,
    pub selected_series: usize,
    pub num_seasons: usize,
}

pub type ProgressTime = DateTime<Utc>;
pub type StartTime = DateTime<Utc>;

pub enum WatchState {
    Idle,
    Watching(StartTime, ProgressTime, process::Child),
}

impl PartialEq for WatchState {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (WatchState::Idle, WatchState::Idle) => true,
            (WatchState::Watching(_, _, _), WatchState::Watching(_, _, _)) => true,
            _ => false,
        }
    }
}

pub struct SeasonState<'a> {
    pub series: Series<'a>,
    pub tracker: SeriesTracker<'a>,
    pub season_num: usize,
    pub watch_time: String,
    pub score: Cow<'a, str>,
    pub watch_state: WatchState,
}

impl<'a> SeasonState<'a> {
    fn new<R, S>(
        remote: &R,
        name: S,
        series: Series<'a>,
        season_num: usize,
    ) -> Result<SeasonState<'a>>
    where
        R: RemoteService + ?Sized,
        S: Into<String>,
    {
        let watch_time = {
            let watch_time_mins = series.info.episodes * series.info.episode_length;
            util::hm_from_mins(watch_time_mins as f32)
        };

        let tracker = SeriesTracker::init(series.info.clone(), name)?;

        let score = match tracker.state.score() {
            Some(score) => remote.score_to_str(score).into_owned().into(),
            None => "??".into(),
        };

        Ok(SeasonState {
            series,
            tracker,
            season_num,
            watch_time,
            score,
            watch_state: WatchState::Idle,
        })
    }

    fn play_next_episode_async<R>(&mut self, remote: &R, config: &Config) -> Result<()>
    where
        R: RemoteService + ?Sized,
    {
        self.tracker.begin_watching(remote, config)?;
        let next_ep = self.tracker.state.watched_eps() + 1;

        let episode = self
            .series
            .get_episode(next_ep)
            .context(err::EpisodeNotFound { episode: next_ep })?;

        let start_time = Utc::now();

        let child = crate::process::open_with_default(episode)
            .context(err::FailedToPlayEpisode { episode: next_ep })?;

        let progress_time = {
            let secs_must_watch =
                (self.series.info.episode_length as f32 * config.episode.pcnt_must_watch) * 60.0;
            let time_must_watch = Duration::seconds(secs_must_watch as i64);

            start_time.add(time_must_watch)
        };

        self.watch_state = WatchState::Watching(start_time, progress_time, child);

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.

use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast::{Item, ItemKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{sym, Ident, Symbol};

declare_tool_lint! {
    pub rustc::DEFAULT_HASH_TYPES,
    Allow,
    "forbid HashMap and HashSet and suggest the FxHash* variants",
    report_in_external_macro: true
}

pub struct DefaultHashTypes {
    map: FxHashMap<Symbol, Symbol>,
}

impl DefaultHashTypes {
    // we are allowed to use `HashMap` and `HashSet` as identifiers for implementing the lint itself
    #[allow(rustc::default_hash_types)]
    pub fn new() -> Self {
        let mut map = FxHashMap::default();
        map.insert(sym::HashMap, sym::FxHashMap);
        map.insert(sym::HashSet, sym::FxHashSet);
        Self { map }
    }
}

impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);

impl EarlyLintPass for DefaultHashTypes {
    fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
        if let Some(replace) = self.map.get(&ident.name) {
            cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
                // FIXME: We can avoid a copy here. Would require us to take String instead of &str.
                let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
                lint.build(&msg)
                    .span_suggestion(
                        ident.span,
                        "use",
                        replace.to_string(),
                        Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
                    )
                    .note(&format!(
                        "a `use rustc_data_structures::fx::{}` may be necessary",
                        replace
                    ))
                    .emit();
            });
        }
    }
}

declare_tool_lint! {
    pub rustc::USAGE_OF_TY_TYKIND,
    Allow,
    "usage of `ty::TyKind` outside of the `ty::sty` module",
    report_in_external_macro: true
}

declare_tool_lint! {
    pub rustc::TY_PASS_BY_REFERENCE,
    Allow,
    "passing `Ty` or `TyCtxt` by reference",
    report_in_external_macro: true
}

declare_tool_lint! {
    pub rustc::USAGE_OF_QUALIFIED_TY,
    Allow,
    "using `ty::{Ty,TyCtxt}` instead of importing it",
    report_in_external_macro: true
}

declare_lint_pass!(TyTyKind => [
    USAGE_OF_TY_TYKIND,
    TY_PASS_BY_REFERENCE,
    USAGE_OF_QUALIFIED_TY,
]);

impl<'tcx> LateLintPass<'tcx> for TyTyKind {
    fn check_path(&mut self, cx: &LateContext<'_>, path: &'tcx Path<'tcx>, _: HirId) {
        let segments = path.segments.iter().rev().skip(1).rev();

        if let Some(last) = segments.last() {
            let span = path.span.with_hi(last.ident.span.hi());
            if lint_ty_kind_usage(cx, last) {
                cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
                    lint.build("usage of `ty::TyKind::<kind>`")
                        .span_suggestion(
                            span,
                            "try using ty::<kind> directly",
                            "ty".to_string(),
                            Applicability::MaybeIncorrect, // ty maybe needs an import
                        )
                        .emit();
                })
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) {
        match &ty.kind {
            TyKind::Path(qpath) => {
                if let QPath::Resolved(_, path) = qpath {
                    if let Some(last) = path.segments.iter().last() {
                        if lint_ty_kind_usage(cx, last) {
                            cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
                                lint.build("usage of `ty::TyKind`")
                                    .help("try using `Ty` instead")
                                    .emit();
                            })
                        } else {
                            if ty.span.from_expansion() {
                                return;
                            }
                            if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
                                if path.segments.len() > 1 {
                                    cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
                                        lint.build(&format!("usage of qualified `ty::{}`", t))
                                            .span_suggestion(
                                                path.span,
                                                "try using it unqualified",
                                                t,
                                                // The import probably needs to be changed
                                                Applicability::MaybeIncorrect,
                                            )
                                            .emit();
                                    })
                                }
                            }
                        }
                    }
                }
            }
            TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => {
                if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
                    if cx.tcx.impl_trait_ref(impl_did).is_some() {
                        return;
                    }
                }
                if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
                    cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| {
                        lint.build(&format!("passing `{}` by reference", t))
                            .span_suggestion(
                                ty.span,
                                "try passing by value",
                                t,
                                // Changing type of function argument
                                Applicability::MaybeIncorrect,
                            )
                            .emit();
                    })
                }
            }
            _ => {}
        }
    }
}

fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool {
    if let Some(res) = segment.res {
        if let Some(did) = res.opt_def_id() {
            return cx.tcx.is_diagnostic_item(sym::TyKind, did);
        }
    }

    false
}

fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
    if let TyKind::Path(qpath) = &ty.kind {
        if let QPath::Resolved(_, path) = qpath {
            let did = path.res.opt_def_id()?;
            if cx.tcx.is_diagnostic_item(sym::Ty, did) {
                return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
            } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
                return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
            }
        }
    }

    None
}

fn gen_args(segment: &PathSegment<'_>) -> String {
    if let Some(args) = &segment.args {
        let lifetimes = args
            .args
            .iter()
            .filter_map(|arg| {
                if let GenericArg::Lifetime(lt) = arg {
                    Some(lt.name.ident().to_string())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        if !lifetimes.is_empty() {
            return format!("<{}>", lifetimes.join(", "));
        }
    }

    String::new()
}

declare_tool_lint! {
    pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
    Allow,
    "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
}

declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);

impl EarlyLintPass for LintPassImpl {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Impl { of_trait: Some(lint_pass), .. } = &item.kind {
            if let Some(last) = lint_pass.path.segments.last() {
                if last.ident.name == sym::LintPass {
                    let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
                    let call_site = expn_data.call_site;
                    if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
                        && call_site.ctxt().outer_expn_data().kind
                            != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
                    {
                        cx.struct_span_lint(
                            LINT_PASS_IMPL_WITHOUT_MACRO,
                            lint_pass.path.span,
                            |lint| {
                                lint.build("implementing `LintPass` by hand")
                                    .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
                                    .emit();
                            },
                        )
                    }
                }
            }
        }
    }
}
<commit_msg>extend `is_ty_or_ty_ctxt` to self types<commit_after>//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.

use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast::{Item, ItemKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath, Ty, TyKind};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{sym, Ident, Symbol};

declare_tool_lint! {
    pub rustc::DEFAULT_HASH_TYPES,
    Allow,
    "forbid HashMap and HashSet and suggest the FxHash* variants",
    report_in_external_macro: true
}

pub struct DefaultHashTypes {
    map: FxHashMap<Symbol, Symbol>,
}

impl DefaultHashTypes {
    // we are allowed to use `HashMap` and `HashSet` as identifiers for implementing the lint itself
    #[allow(rustc::default_hash_types)]
    pub fn new() -> Self {
        let mut map = FxHashMap::default();
        map.insert(sym::HashMap, sym::FxHashMap);
        map.insert(sym::HashSet, sym::FxHashSet);
        Self { map }
    }
}

impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);

impl EarlyLintPass for DefaultHashTypes {
    fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
        if let Some(replace) = self.map.get(&ident.name) {
            cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
                // FIXME: We can avoid a copy here. Would require us to take String instead of &str.
                let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
                lint.build(&msg)
                    .span_suggestion(
                        ident.span,
                        "use",
                        replace.to_string(),
                        Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
                    )
                    .note(&format!(
                        "a `use rustc_data_structures::fx::{}` may be necessary",
                        replace
                    ))
                    .emit();
            });
        }
    }
}

declare_tool_lint! {
    pub rustc::USAGE_OF_TY_TYKIND,
    Allow,
    "usage of `ty::TyKind` outside of the `ty::sty` module",
    report_in_external_macro: true
}

declare_tool_lint! {
    pub rustc::TY_PASS_BY_REFERENCE,
    Allow,
    "passing `Ty` or `TyCtxt` by reference",
    report_in_external_macro: true
}

declare_tool_lint! {
    pub rustc::USAGE_OF_QUALIFIED_TY,
    Allow,
    "using `ty::{Ty,TyCtxt}` instead of importing it",
    report_in_external_macro: true
}

declare_lint_pass!(TyTyKind => [
    USAGE_OF_TY_TYKIND,
    TY_PASS_BY_REFERENCE,
    USAGE_OF_QUALIFIED_TY,
]);

impl<'tcx> LateLintPass<'tcx> for TyTyKind {
    fn check_path(&mut self, cx: &LateContext<'_>, path: &'tcx Path<'tcx>, _: HirId) {
        let segments = path.segments.iter().rev().skip(1).rev();

        if let Some(last) = segments.last() {
            let span = path.span.with_hi(last.ident.span.hi());
            if lint_ty_kind_usage(cx, last) {
                cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
                    lint.build("usage of `ty::TyKind::<kind>`")
                        .span_suggestion(
                            span,
                            "try using ty::<kind> directly",
                            "ty".to_string(),
                            Applicability::MaybeIncorrect, // ty maybe needs an import
                        )
                        .emit();
                })
            }
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) {
        match &ty.kind {
            TyKind::Path(qpath) => {
                if let QPath::Resolved(_, path) = qpath {
                    if let Some(last) = path.segments.iter().last() {
                        if lint_ty_kind_usage(cx, last) {
                            cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
                                lint.build("usage of `ty::TyKind`")
                                    .help("try using `Ty` instead")
                                    .emit();
                            })
                        } else {
                            if ty.span.from_expansion() {
                                return;
                            }
                            if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
                                if path.segments.len() > 1 {
                                    cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
                                        lint.build(&format!("usage of qualified `ty::{}`", t))
                                            .span_suggestion(
                                                path.span,
                                                "try using it unqualified",
                                                t,
                                                // The import probably needs to be changed
                                                Applicability::MaybeIncorrect,
                                            )
                                            .emit();
                                    })
                                }
                            }
                        }
                    }
                }
            }
            TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => {
                if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
                    if cx.tcx.impl_trait_ref(impl_did).is_some() {
                        return;
                    }
                }
                if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
                    cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| {
                        lint.build(&format!("passing `{}` by reference", t))
                            .span_suggestion(
                                ty.span,
                                "try passing by value",
                                t,
                                // Changing type of function argument
                                Applicability::MaybeIncorrect,
                            )
                            .emit();
                    })
                }
            }
            _ => {}
        }
    }
}

fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool {
    if let Some(res) = segment.res {
        if let Some(did) = res.opt_def_id() {
            return cx.tcx.is_diagnostic_item(sym::TyKind, did);
        }
    }

    false
}

fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
    if let TyKind::Path(qpath) = &ty.kind {
        if let QPath::Resolved(_, path) = qpath {
            match path.res {
                Res::Def(_, did) => {
                    if cx.tcx.is_diagnostic_item(sym::Ty, did) {
                        return Some(format!("Ty{}", gen_args(path.segments.last().unwrap())));
                    } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, did) {
                        return Some(format!("TyCtxt{}", gen_args(path.segments.last().unwrap())));
                    }
                }
                // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
                Res::SelfTy(None, Some((did, _))) => {
                    if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
                        if cx.tcx.is_diagnostic_item(sym::Ty, adt.did) {
                            return Some(format!("Ty<{}>", substs[0]));
                        } else if cx.tcx.is_diagnostic_item(sym::TyCtxt, adt.did) {
                            return Some(format!("TyCtxt<{}>", substs[0]));
                        }
                    }
                }
                _ => (),
            }
        }
    }

    None
}

fn gen_args(segment: &PathSegment<'_>) -> String {
    if let Some(args) = &segment.args {
        let lifetimes = args
            .args
            .iter()
            .filter_map(|arg| {
                if let GenericArg::Lifetime(lt) = arg {
                    Some(lt.name.ident().to_string())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        if !lifetimes.is_empty() {
            return format!("<{}>", lifetimes.join(", "));
        }
    }

    String::new()
}

declare_tool_lint! {
    pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO,
    Allow,
    "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros"
}

declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);

impl EarlyLintPass for LintPassImpl {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Impl { of_trait: Some(lint_pass), .. } = &item.kind {
            if let Some(last) = lint_pass.path.segments.last() {
                if last.ident.name == sym::LintPass {
                    let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
                    let call_site = expn_data.call_site;
                    if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
                        && call_site.ctxt().outer_expn_data().kind
                            != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
                    {
                        cx.struct_span_lint(
                            LINT_PASS_IMPL_WITHOUT_MACRO,
                            lint_pass.path.span,
                            |lint| {
                                lint.build("implementing `LintPass` by hand")
                                    .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
                                    .emit();
                            },
                        )
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(const_fn)]

extern crate orbclient;

use std::env;
use std::fs::{self, File};
use std::process::Command;
use std::thread;

use orbclient::{BmpFile, Color, EventOption, Window};

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(183, 189, 203);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let mut packages: Vec<Package> = Vec::new();

    for entry_result in fs::read_dir("/apps/").unwrap() {
        let entry = entry_result.unwrap();
        if entry.file_type().unwrap().is_dir() {
            packages.push(Package::from_path(&("/apps/".to_string() + entry.file_name().to_str().unwrap())));
        }
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &BmpFile, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        if package.icon.has_data() {
            let y = h as isize - package.icon.height() as isize;

            if i == selected {
                window.rect(x as i32, y as i32,
                                  package.icon.width() as u32, package.icon.height() as u32,
                                  HIGHLIGHT_COLOR);
            }

            window.image(x as i32, y as i32,
                        package.icon.width() as u32,
                        package.icon.height() as u32,
                        &package.icon);
            x = x + package.icon.width() as i32;
            i += 1;
        }
    }

    if shutdown.has_data() {
        x = w as i32 - shutdown.width() as i32;
        let y = h as isize - shutdown.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              shutdown.width() as u32, shutdown.height() as u32,
                              HIGHLIGHT_COLOR);
        }

        window.image(x as i32, y as i32,
                        shutdown.width() as u32, shutdown.height() as u32,
                        &shutdown);
        x = x + shutdown.width() as i32;
        i += 1;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, packages: &Vec<Package>, mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        if mouse_y >= y as i32 && mouse_y < y + 32 {
            window.rect(0, y, w, 32, HIGHLIGHT_COLOR);
        }

        if package.icon.has_data() {
            window.image(0, y, package.icon.width() as u32, package.icon.height() as u32, &package.icon);
        }

        let mut c_x = 40;
        for c in package.name.chars() {
            window.char(c_x as i32, y + 8, c, TEXT_COLOR);
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                for package in packages.iter() {
                    println!("{:?}: {}", package.binary, package.icon.has_data());
                }

                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();

                draw_chooser(&mut window, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if package.icon.has_data() {
                                            if mouse_event.y >= y && mouse_event.y < y + 32 {
                                                if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                    println!("{}: Failed to launch: {}", package.binary, err);
                                                }
                                                break 'choosing;
                                            }
                                            y += 32;
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = BmpFile::from_path("/ui/actions/system-shutdown.bmp");
        if ! shutdown.has_data() {
            println!("launcher: failed to read shutdown icon");
        }

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    let y = window.height() as i32 - package.icon.height() as i32;
                                    if mouse_event.y >= y && mouse_event.x >= x &&
                                       mouse_event.x < x + package.icon.width() as i32 {
                                        now_selected = i;
                                    }
                                    x = x + package.icon.width() as i32;
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                x = window.width() as i32 - shutdown.width() as i32;
                                let y = window.height() as i32 - shutdown.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + shutdown.width() as i32 {
                                       now_selected = i;
                                }
                                i += 1;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    if i == selected {
                                        if let Err(err) = Command::new(&package.binary).spawn() {
                                            println!("{}: Failed to launch: {}", package.binary, err);
                                        }
                                    }
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                if i == selected {
                                       File::create("acpi:off");
                                }
                                i += 1;
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<commit_msg>Update color scheme<commit_after>#![feature(const_fn)]

extern crate orbclient;

use std::env;
use std::fs::{self, File};
use std::process::Command;
use std::thread;

use orbclient::{BmpFile, Color, EventOption, Window};

use package::Package;

pub mod package;

const BAR_COLOR: Color = Color::rgb(40, 45, 57);
const BAR_HIGHLIGHT_COLOR: Color = Color::rgb(80, 86, 102);
const TEXT_COLOR: Color = Color::rgb(204, 210, 224);
const TEXT_HIGHLIGHT_COLOR: Color = Color::rgb(235, 241, 255);

//TODO: Implement display size in orbclient
#[cfg(target_os = "redox")]
fn get_display_size() -> (i32, i32) {
    match File::open("display:") {
        Ok(display) => {
            let path = display.path().map(|path| path.into_os_string().into_string().unwrap_or(String::new())).unwrap_or(String::new());
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(0).unwrap_or("").parse::<i32>().unwrap_or(0);
            let height = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
            (width, height)
        },
        Err(err) => panic!("launcher: failed to get display size: {}", err)
    }
}

#[cfg(not(target_os = "redox"))]
fn get_display_size() -> (i32, i32) {
    panic!("launcher: failed to get display size")
}

fn get_packages() -> Vec<Package> {
    let mut packages: Vec<Package> = Vec::new();

    for entry_result in fs::read_dir("/apps/").unwrap() {
        let entry = entry_result.unwrap();
        if entry.file_type().unwrap().is_dir() {
            packages.push(Package::from_path(&("/apps/".to_string() + entry.file_name().to_str().unwrap())));
        }
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &BmpFile, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(BAR_COLOR);

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        if package.icon.has_data() {
            let y = h as isize - package.icon.height() as isize;

            if i == selected {
                window.rect(x as i32, y as i32,
                                  package.icon.width() as u32, package.icon.height() as u32,
                                  BAR_HIGHLIGHT_COLOR);
            }

            window.image(x as i32, y as i32,
                        package.icon.width() as u32,
                        package.icon.height() as u32,
                        &package.icon);
            x = x + package.icon.width() as i32;
            i += 1;
        }
    }

    if shutdown.has_data() {
        x = w as i32 - shutdown.width() as i32;
        let y = h as isize - shutdown.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              shutdown.width() as u32, shutdown.height() as u32,
                              BAR_HIGHLIGHT_COLOR);
        }

        window.image(x as i32, y as i32,
                        shutdown.width() as u32, shutdown.height() as u32,
                        &shutdown);
        x = x + shutdown.width() as i32;
        i += 1;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, packages: &Vec<Package>, mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(BAR_COLOR);

    let mut y = 0;
    for package in packages.iter() {
        let highlight = mouse_y >= y as i32 && mouse_y < y + 32;

        if highlight {
            window.rect(0, y, w, 32, BAR_HIGHLIGHT_COLOR);
        }

        if package.icon.has_data() {
            window.image(0, y, package.icon.width() as u32, package.icon.height() as u32, &package.icon);
        }

        let mut c_x = 40;
        for c in package.name.chars() {
            window.char(c_x as i32, y + 8, c, if highlight { TEXT_HIGHLIGHT_COLOR } else { TEXT_COLOR });
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                for package in packages.iter() {
                    println!("{:?}: {}", package.binary, package.icon.has_data());
                }

                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();

                draw_chooser(&mut window, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if package.icon.has_data() {
                                            if mouse_event.y >= y && mouse_event.y < y + 32 {
                                                if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                    println!("{}: Failed to launch: {}", package.binary, err);
                                                }
                                                break 'choosing;
                                            }
                                            y += 32;
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = BmpFile::from_path("/ui/actions/system-shutdown.bmp");
        if ! shutdown.has_data() {
            println!("launcher: failed to read shutdown icon");
        }

        let (width, height) = get_display_size();
        let mut window = Window::new(0, height - 32, width as u32, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    let y = window.height() as i32 - package.icon.height() as i32;
                                    if mouse_event.y >= y && mouse_event.x >= x &&
                                       mouse_event.x < x + package.icon.width() as i32 {
                                        now_selected = i;
                                    }
                                    x = x + package.icon.width() as i32;
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                x = window.width() as i32 - shutdown.width() as i32;
                                let y = window.height() as i32 - shutdown.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + shutdown.width() as i32 {
                                       now_selected = i;
                                }
                                i += 1;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    if i == selected {
                                        if let Err(err) = Command::new(&package.binary).spawn() {
                                            println!("{}: Failed to launch: {}", package.binary, err);
                                        }
                                    }
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                if i == selected {
                                       File::create("acpi:off");
                                }
                                i += 1;
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<|endoftext|>