<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! A minimal [Matrix](https://matrix.org/) client library.
//!
//! # Usage
//!
//! Begin by creating a `Client` type, usually using the `https` method for a client that supports
//! secure connections, and then logging in:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, None);
//!
//!     let session = client
//!         .log_in("@alice:example.com", "secret", None, None)
//!         .await?;
//!
//!     // You're now logged in! Write the session to a file if you want to restore it later.
//!     // Then start using the API!
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! };
//! ```
//!
//! You can also pass an existing access token to the `Client` constructor to restore a previous
//! session rather than calling `log_in`. This can also be used to create a session for an
//! application service that does not need to log in, but uses the access_token directly:
//!
//! ```no_run
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, Some("as_access_token".into()));
//!
//!     // make calls to the API
//! };
//! ```
//!
//! For the standard use case of synchronizing with the homeserver (i.e. getting all the latest
//! events), use the `Client::sync`:
//!
//! ```ignore
//! use std::time::Duration;
//!
//! # use ruma_client::Client;
//! # use ruma::presence::PresenceState;
//! # use tokio_stream::{StreamExt as _};
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! # let next_batch_token = String::new();
//! # async {
//! let mut sync_stream = Box::pin(client.sync(
//!     None,
//!     next_batch_token,
//!     &PresenceState::Online,
//!     Some(Duration::from_secs(30)),
//! ));
//! while let Some(response) = sync_stream.try_next().await? {
//!     // Do something with the data in the response...
//! }
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! # };
//! ```
//!
//! The `Client` type also provides methods for registering a new account if you don't already have
//! one with the given homeserver.
//!
//! Beyond these basic convenience methods, `ruma-client` gives you access to the entire Matrix
//! client-server API via the `api` module. Each leaf module under this tree of modules contains
//! the necessary types for one API endpoint. Simply call the module's `call` method, passing it
//! the logged in `Client` and the relevant `Request` type. `call` will return a future that will
//! resolve to the relevant `Response` type.
//!
//! For example:
//!
//! ```no_run
//! # use ruma_client::Client;
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! use std::convert::TryFrom;
//!
//! use ruma::{
//!     api::client::r0::alias::get_alias,
//!     room_alias_id, room_id,
//! };
//!
//! async {
//!     let response = client
//!         .request(get_alias::Request::new(&room_alias_id!("#example_room:example.com")))
//!         .await?;
//!
//!     assert_eq!(response.room_id, room_id!("!n8f893n9:example.com"));
//! #   Result::<(), ruma_client::Error<_>>::Ok(())
//! }
//! # ;
//! ```

#![warn(rust_2018_idioms)]
#![deny(missing_debug_implementations, missing_docs)]

use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex},
};

use assign::assign;
use http::{uri::Uri, Response as HttpResponse};
use hyper::client::{Client as HyperClient, HttpConnector};
use ruma_api::{AuthScheme, OutgoingRequest, SendAccessToken};
use ruma_serde::urlencoded;

#[cfg(feature = "client-api")]
mod client_api;
mod error;

pub use self::error::Error;

#[cfg(not(feature = "_tls"))]
type Connector = HttpConnector;

#[cfg(feature = "tls-native")]
type Connector = hyper_tls::HttpsConnector<HttpConnector>;

#[cfg(feature = "_tls-rustls")]
type Connector = hyper_rustls::HttpsConnector<HttpConnector>;

fn create_connector() -> Connector {
    #[cfg(not(feature = "_tls"))]
    let connector = HttpConnector::new();

    #[cfg(feature = "tls-native")]
    let connector = hyper_tls::HttpsConnector::new();

    #[cfg(feature = "tls-rustls-native-roots")]
    let connector = hyper_rustls::HttpsConnector::with_native_roots();

    #[cfg(feature = "tls-rustls-webpki-roots")]
    let connector = hyper_rustls::HttpsConnector::with_webpki_roots();

    connector
}

/// A client for the Matrix client-server API.
#[derive(Clone, Debug)]
pub struct Client(Arc<ClientData>);

/// Data contained in Client's Rc
#[derive(Debug)]
struct ClientData {
    /// The URL of the homeserver to connect to.
    homeserver_url: Uri,

    /// The underlying HTTP client.
    hyper: HyperClient<Connector>,

    /// User session data.
    access_token: Mutex<Option<String>>,
}

impl Client {
    /// Creates a new client.
    pub fn new(homeserver_url: Uri, access_token: Option<String>) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            hyper: HyperClient::builder().build(create_connector()),
            access_token: Mutex::new(access_token),
        }))
    }

    /// Creates a new client using the given `hyper::client::Builder`.
    ///
    /// This allows the user to configure the details of HTTP as desired.
    pub fn custom(
        client_builder: &hyper::client::Builder,
        homeserver_url: Uri,
        access_token: Option<String>,
    ) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            hyper: client_builder.build(create_connector()),
            access_token: Mutex::new(access_token),
        }))
    }

    /// Get a copy of the current `access_token`, if any.
    ///
    /// Useful for serializing and persisting the session to be restored later.
    pub fn access_token(&self) -> Option<String> {
        self.0.access_token.lock().expect("session mutex was poisoned").clone()
    }

    /// Makes a request to a Matrix API endpoint.
    pub async fn request<Request: OutgoingRequest>(
        &self,
        request: Request,
    ) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
        self.request_with_url_params(request, None).await
    }

    /// Makes a request to a Matrix API endpoint including additional URL parameters.
    pub async fn request_with_url_params<Request: OutgoingRequest>(
        &self,
        request: Request,
        extra_params: Option<BTreeMap<String, String>>,
    ) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
        let client = self.0.clone();
        let mut http_request = {
            let lock;
            let access_token = if Request::METADATA.authentication == AuthScheme::AccessToken {
                lock = client.access_token.lock().unwrap();
                if let Some(access_token) = &*lock {
                    SendAccessToken::IfRequired(access_token.as_str())
                } else {
                    return Err(Error::AuthenticationRequired);
                }
            } else {
                SendAccessToken::None
            };

            request.try_into_http_request::<Vec<u8>>(
                &client.homeserver_url.to_string(),
                access_token,
            )?
        };

        let extra_params = urlencoded::to_string(extra_params).unwrap();
        let uri = http_request.uri_mut();
        let new_path_and_query = match uri.query() {
            Some(params) => format!("{}?{}&{}", uri.path(), params, extra_params),
            None => format!("{}?{}", uri.path(), extra_params),
        };
        *uri = Uri::from_parts(assign!(uri.clone().into_parts(), {
            path_and_query: Some(new_path_and_query.parse()?),
        }))?;

        let hyper_response = client.hyper.request(http_request.map(hyper::Body::from)).await?;
        let (head, body) = hyper_response.into_parts();

        // FIXME: Use aggregate instead of to_bytes once serde_json can parse from a reader at a
        // comparable speed as reading from a slice: https://github.com/serde-rs/json/issues/160
        let full_body = hyper::body::to_bytes(body).await?;
        let full_response = HttpResponse::from_parts(head, full_body);

        Ok(ruma_api::IncomingResponse::try_from_http_response(full_response)?)
    }
}
<commit_msg>client: Fix crate docs<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! A minimal [Matrix](https://matrix.org/) client library.
//!
//! # Usage
//!
//! Begin by creating a `Client` type, usually using the `https` method for a client that supports
//! secure connections, and then logging in:
//!
//! ```ignore
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, None);
//!
//!     let session = client
//!         .log_in("@alice:example.com", "secret", None, None)
//!         .await?;
//!
//!     // You're now logged in! Write the session to a file if you want to restore it later.
//!     // Then start using the API!
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! };
//! ```
//!
//! You can also pass an existing access token to the `Client` constructor to restore a previous
//! session rather than calling `log_in`. This can also be used to create a session for an
//! application service that does not need to log in, but uses the access_token directly:
//!
//! ```no_run
//! use ruma_client::Client;
//!
//! let work = async {
//!     let homeserver_url = "https://example.com".parse().unwrap();
//!     let client = Client::new(homeserver_url, Some("as_access_token".into()));
//!
//!     // make calls to the API
//! };
//! ```
//!
//! For the standard use case of synchronizing with the homeserver (i.e. getting all the latest
//! events), use the `Client::sync` method:
//!
//! ```ignore
//! use std::time::Duration;
//!
//! # use ruma_client::Client;
//! # use ruma::presence::PresenceState;
//! # use tokio_stream::{StreamExt as _};
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! # let next_batch_token = String::new();
//! # async {
//! let mut sync_stream = Box::pin(client.sync(
//!     None,
//!     next_batch_token,
//!     &PresenceState::Online,
//!     Some(Duration::from_secs(30)),
//! ));
//! while let Some(response) = sync_stream.try_next().await? {
//!     // Do something with the data in the response...
//! }
//! # Result::<(), ruma_client::Error<_>>::Ok(())
//! # };
//! ```
//!
//! The `Client` type also provides methods for registering a new account if you don't already have
//! one with the given homeserver.
//!
//! Beyond these basic convenience methods, `ruma-client` gives you access to the entire Matrix
//! client-server API via the `request` method. You can pass it any of the `Request` types found in
//! `ruma::api::*` and get back a corresponding response from the homeserver.
//!
//! For example:
//!
//! ```no_run
//! # use ruma_client::Client;
//! # let homeserver_url = "https://example.com".parse().unwrap();
//! # let client = Client::new(homeserver_url, None);
//! use std::convert::TryFrom;
//!
//! use ruma::{
//!     api::client::r0::alias::get_alias,
//!     room_alias_id, room_id,
//! };
//!
//! async {
//!     let response = client
//!         .request(get_alias::Request::new(&room_alias_id!("#example_room:example.com")))
//!         .await?;
//!
//!     assert_eq!(response.room_id, room_id!("!n8f893n9:example.com"));
//! #   Result::<(), ruma_client::Error<_>>::Ok(())
//! }
//! # ;
//! ```

#![warn(rust_2018_idioms)]
#![deny(missing_debug_implementations, missing_docs)]

use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex},
};

use assign::assign;
use http::{uri::Uri, Response as HttpResponse};
use hyper::client::{Client as HyperClient, HttpConnector};
use ruma_api::{AuthScheme, OutgoingRequest, SendAccessToken};
use ruma_serde::urlencoded;

#[cfg(feature = "client-api")]
mod client_api;
mod error;

pub use self::error::Error;

#[cfg(not(feature = "_tls"))]
type Connector = HttpConnector;

#[cfg(feature = "tls-native")]
type Connector = hyper_tls::HttpsConnector<HttpConnector>;

#[cfg(feature = "_tls-rustls")]
type Connector = hyper_rustls::HttpsConnector<HttpConnector>;

fn create_connector() -> Connector {
    #[cfg(not(feature = "_tls"))]
    let connector = HttpConnector::new();

    #[cfg(feature = "tls-native")]
    let connector = hyper_tls::HttpsConnector::new();

    #[cfg(feature = "tls-rustls-native-roots")]
    let connector = hyper_rustls::HttpsConnector::with_native_roots();

    #[cfg(feature = "tls-rustls-webpki-roots")]
    let connector = hyper_rustls::HttpsConnector::with_webpki_roots();

    connector
}

/// A client for the Matrix client-server API.
#[derive(Clone, Debug)]
pub struct Client(Arc<ClientData>);

/// Data contained in Client's Rc
#[derive(Debug)]
struct ClientData {
    /// The URL of the homeserver to connect to.
    homeserver_url: Uri,

    /// The underlying HTTP client.
    hyper: HyperClient<Connector>,

    /// User session data.
    access_token: Mutex<Option<String>>,
}

impl Client {
    /// Creates a new client.
    pub fn new(homeserver_url: Uri, access_token: Option<String>) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            hyper: HyperClient::builder().build(create_connector()),
            access_token: Mutex::new(access_token),
        }))
    }

    /// Creates a new client using the given `hyper::client::Builder`.
    ///
    /// This allows the user to configure the details of HTTP as desired.
    pub fn custom(
        client_builder: &hyper::client::Builder,
        homeserver_url: Uri,
        access_token: Option<String>,
    ) -> Self {
        Self(Arc::new(ClientData {
            homeserver_url,
            hyper: client_builder.build(create_connector()),
            access_token: Mutex::new(access_token),
        }))
    }

    /// Get a copy of the current `access_token`, if any.
    ///
    /// Useful for serializing and persisting the session to be restored later.
    pub fn access_token(&self) -> Option<String> {
        self.0.access_token.lock().expect("session mutex was poisoned").clone()
    }

    /// Makes a request to a Matrix API endpoint.
    pub async fn request<Request: OutgoingRequest>(
        &self,
        request: Request,
    ) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
        self.request_with_url_params(request, None).await
    }

    /// Makes a request to a Matrix API endpoint including additional URL parameters.
    pub async fn request_with_url_params<Request: OutgoingRequest>(
        &self,
        request: Request,
        extra_params: Option<BTreeMap<String, String>>,
    ) -> Result<Request::IncomingResponse, Error<Request::EndpointError>> {
        let client = self.0.clone();
        let mut http_request = {
            let lock;
            let access_token = if Request::METADATA.authentication == AuthScheme::AccessToken {
                lock = client.access_token.lock().unwrap();
                if let Some(access_token) = &*lock {
                    SendAccessToken::IfRequired(access_token.as_str())
                } else {
                    return Err(Error::AuthenticationRequired);
                }
            } else {
                SendAccessToken::None
            };

            request.try_into_http_request::<Vec<u8>>(
                &client.homeserver_url.to_string(),
                access_token,
            )?
        };

        let extra_params = urlencoded::to_string(extra_params).unwrap();
        let uri = http_request.uri_mut();
        let new_path_and_query = match uri.query() {
            Some(params) => format!("{}?{}&{}", uri.path(), params, extra_params),
            None => format!("{}?{}", uri.path(), extra_params),
        };
        *uri = Uri::from_parts(assign!(uri.clone().into_parts(), {
            path_and_query: Some(new_path_and_query.parse()?),
        }))?;

        let hyper_response = client.hyper.request(http_request.map(hyper::Body::from)).await?;
        let (head, body) = hyper_response.into_parts();

        // FIXME: Use aggregate instead of to_bytes once serde_json can parse from a reader at a
        // comparable speed as reading from a slice: https://github.com/serde-rs/json/issues/160
        let full_body = hyper::body::to_bytes(body).await?;
        let full_response = HttpResponse::from_parts(head, full_body);

        Ok(ruma_api::IncomingResponse::try_from_http_response(full_response)?)
    }
}
<|endoftext|>


<commit_before>//
// sprocketnes/input.rs
//
// Author: Patrick Walton
//

use mem::Mem;

use sdl::event::{KeyDownEvent, KeyUpEvent, KeyboardEvent, NoEvent};
use sdl::event;
use sdl::keyboard::{SDLKDown, SDLKEscape, SDLKLeft, SDLKRShift, SDLKReturn, SDLKRight, SDLKUp};
use sdl::keyboard::{SDLKx, SDLKz};

//
// The "strobe state": the order in which the NES reads the buttons.
//

const STROBE_STATE_A: u8        = 0;
const STROBE_STATE_B: u8        = 1;
const STROBE_STATE_SELECT: u8   = 2;
const STROBE_STATE_START: u8    = 3;
const STROBE_STATE_UP: u8       = 4;
const STROBE_STATE_DOWN: u8     = 5;
const STROBE_STATE_LEFT: u8     = 6;
const STROBE_STATE_RIGHT: u8    = 7;

struct StrobeState(u8);

impl StrobeState {
    // Given a GamePadState structure, returns the state of the given button.
    fn get(&self, state: &GamePadState) -> bool {
        match **self {
            STROBE_STATE_A      => state.a,
            STROBE_STATE_B      => state.b,
            STROBE_STATE_SELECT => state.select,
            STROBE_STATE_START  => state.start,
            STROBE_STATE_UP     => state.up,
            STROBE_STATE_DOWN   => state.down,
            STROBE_STATE_LEFT   => state.left,
            STROBE_STATE_RIGHT  => state.right,
            _                   => die!(~"shouldn't happen")
        }
    }

    fn next(&mut self) {
        *self = StrobeState((**self + 1) & 7);
    }

    fn reset(&mut self) {
        *self = StrobeState(STROBE_STATE_A);
    }
}

//
// The standard NES game pad state
//

pub struct GamePadState {
    left: bool,
    down: bool,
    up: bool,
    right: bool,
    a: bool,
    b: bool,
    select: bool,
    start: bool,

    strobe_state: StrobeState,
}

pub struct Input {
    gamepad_0: GamePadState
}

pub enum InputResult {
    Continue,   // Keep playing.
    Quit,       // Quit the emulator.
}

impl Input {
    static fn new() -> Input {
        Input {
            gamepad_0: GamePadState {
                left: false,
                down: false,
                up: false,
                right: false,
                a: false,
                b: false,
                select: false,
                start: false,

                strobe_state: StrobeState(STROBE_STATE_A)
            }
        }
    }

    fn handle_gamepad_event(&mut self, key_event: &KeyboardEvent, down: bool) {
        match key_event.keycode {
            SDLKLeft   => self.gamepad_0.left   = down,
            SDLKDown   => self.gamepad_0.down   = down,
            SDLKUp     => self.gamepad_0.up     = down,
            SDLKRight  => self.gamepad_0.right  = down,
            SDLKz      => self.gamepad_0.a      = down,
            SDLKx      => self.gamepad_0.b      = down,
            SDLKRShift => self.gamepad_0.select = down,
            SDLKReturn => self.gamepad_0.start  = down,
            _          => {}
        }
    }

    fn check_input(&mut self) -> InputResult {
        loop {
            match event::poll_event() {
                NoEvent => break,
                KeyDownEvent(ref key_event) => {
                    self.handle_gamepad_event(key_event, true);

                    if key_event.keycode == SDLKEscape {
                        return Quit;
                    }
                }
                KeyUpEvent(ref key_event) => self.handle_gamepad_event(key_event, false),
                _ => {}
            }
        }
        return Continue;
    }
}

impl Mem for Input {
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr == 0x4016 {
            let result = self.gamepad_0.strobe_state.get(&self.gamepad_0) as u8;
            self.gamepad_0.strobe_state.next();
            result
        } else {
            0
        }
    }

    fn storeb(&mut self, addr: u16, _: u8) {
        if addr == 0x4016 {
            // FIXME: This is not really accurate; you're supposed to not reset until you see
            // 1 strobed than 0. But I doubt this will break anything.
            self.gamepad_0.strobe_state.reset();
        }
    }
}

<commit_msg>Handle the SDL quit event<commit_after>//
// sprocketnes/input.rs
//
// Author: Patrick Walton
//

use mem::Mem;

use sdl::event::{KeyDownEvent, KeyUpEvent, KeyboardEvent, NoEvent, QuitEvent};
use sdl::event;
use sdl::keyboard::{SDLKDown, SDLKEscape, SDLKLeft, SDLKRShift, SDLKReturn, SDLKRight, SDLKUp};
use sdl::keyboard::{SDLKx, SDLKz};

//
// The "strobe state": the order in which the NES reads the buttons.
//

const STROBE_STATE_A: u8        = 0;
const STROBE_STATE_B: u8        = 1;
const STROBE_STATE_SELECT: u8   = 2;
const STROBE_STATE_START: u8    = 3;
const STROBE_STATE_UP: u8       = 4;
const STROBE_STATE_DOWN: u8     = 5;
const STROBE_STATE_LEFT: u8     = 6;
const STROBE_STATE_RIGHT: u8    = 7;

struct StrobeState(u8);

impl StrobeState {
    // Given a GamePadState structure, returns the state of the given button.
    fn get(&self, state: &GamePadState) -> bool {
        match **self {
            STROBE_STATE_A      => state.a,
            STROBE_STATE_B      => state.b,
            STROBE_STATE_SELECT => state.select,
            STROBE_STATE_START  => state.start,
            STROBE_STATE_UP     => state.up,
            STROBE_STATE_DOWN   => state.down,
            STROBE_STATE_LEFT   => state.left,
            STROBE_STATE_RIGHT  => state.right,
            _                   => die!(~"shouldn't happen")
        }
    }

    fn next(&mut self) {
        *self = StrobeState((**self + 1) & 7);
    }

    fn reset(&mut self) {
        *self = StrobeState(STROBE_STATE_A);
    }
}

//
// The standard NES game pad state
//

pub struct GamePadState {
    left: bool,
    down: bool,
    up: bool,
    right: bool,
    a: bool,
    b: bool,
    select: bool,
    start: bool,

    strobe_state: StrobeState,
}

pub struct Input {
    gamepad_0: GamePadState
}

pub enum InputResult {
    Continue,   // Keep playing.
    Quit,       // Quit the emulator.
}

impl Input {
    static fn new() -> Input {
        Input {
            gamepad_0: GamePadState {
                left: false,
                down: false,
                up: false,
                right: false,
                a: false,
                b: false,
                select: false,
                start: false,

                strobe_state: StrobeState(STROBE_STATE_A)
            }
        }
    }

    fn handle_gamepad_event(&mut self, key_event: &KeyboardEvent, down: bool) {
        match key_event.keycode {
            SDLKLeft   => self.gamepad_0.left   = down,
            SDLKDown   => self.gamepad_0.down   = down,
            SDLKUp     => self.gamepad_0.up     = down,
            SDLKRight  => self.gamepad_0.right  = down,
            SDLKz      => self.gamepad_0.a      = down,
            SDLKx      => self.gamepad_0.b      = down,
            SDLKRShift => self.gamepad_0.select = down,
            SDLKReturn => self.gamepad_0.start  = down,
            _          => {}
        }
    }

    fn check_input(&mut self) -> InputResult {
        loop {
            match event::poll_event() {
                NoEvent => break,
                KeyDownEvent(ref key_event) => {
                    self.handle_gamepad_event(key_event, true);

                    if key_event.keycode == SDLKEscape {
                        return Quit;
                    }
                }
                KeyUpEvent(ref key_event) => self.handle_gamepad_event(key_event, false),
                QuitEvent => return Quit,
                _ => {}
            }
        }
        return Continue;
    }
}

impl Mem for Input {
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr == 0x4016 {
            let result = self.gamepad_0.strobe_state.get(&self.gamepad_0) as u8;
            self.gamepad_0.strobe_state.next();
            result
        } else {
            0
        }
    }

    fn storeb(&mut self, addr: u16, _: u8) {
        if addr == 0x4016 {
            // FIXME: This is not really accurate; you're supposed to not reset until you see
            // 1 strobed than 0. But I doubt this will break anything.
            self.gamepad_0.strobe_state.reset();
        }
    }
}

<|endoftext|>


<commit_before>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    print!("{:?}", asset_dir);

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        let a = format!("{:?}", rsi);
        let b = "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }";
        if a != b {
            println!("{:?}\n{:?}", a, b);
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<commit_msg>Remove that<commit_after>extern crate shared;

use shared::assets::*;
use std::env;

#[test]
fn test_binary() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    load_from_dir(asset_dir);

    if let Some(Asset::Binary(ref vec)) = get_asset("test") {
        if vec != &vec![104, 114, 114, 114, 114, 109, 10] {
            panic!("Binary data did not match test file.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}

#[test]
fn test_rsi() {
    let mut asset_dir = env::current_dir().expect("Unable to find executable path.");
    asset_dir.push("tests");
    asset_dir.push("data");

    load_from_dir(asset_dir);

    if let Some(Asset::Rsi(ref rsi)) = get_asset("testrsi.rs.rsi") {
        let a = format!("{:?}", rsi);
        let b = "Rsi { size: (32, 32), states: {\"ByeThere\": State { full name: ByeThere, size: (32, 32), dir: 1, flags: []}, \"HiThere\": State { full name: HiThere, size: (32, 32), dir: 1, flags: []}} }";
        if a != b {
            println!("{:?}\n{:?}", a, b);
            panic!("RSI metadata incorrect.");
        }
    } else {
        panic!("Unable to get test file.");
    }
}
<|endoftext|>


<commit_before>use num::{one, One, Integer, BigUint};
use num::bigint::{ToBigUint};
use rand::Rng;

use utils::modular::power::Power;

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Result {
    PropablyPrime,
    Composite,
}

impl Result {
    pub fn is_composite(&self) -> bool {
        match *self {
            Result::Composite => true,
            _ => false
        }
    }
}

pub trait PrimeTest {
    fn test(&mut self, num: &BigUint) -> Result;

    fn test_loop(&mut self, num: &BigUint, times: usize) -> Result {
        for _ in 0..times {
            if self.test(&num).is_composite() { return Result::Composite }
        }

        Result::PropablyPrime
    }
}

pub struct Fermat<'a, T: Rng + 'a>(pub &'a mut T);

impl<'a, T: Rng + 'a> PrimeTest for Fermat<'a, T> {
    fn test(&mut self, num: &BigUint) -> Result {
        let base = self.0.next_u64().to_biguint().unwrap();
        let num_1 = num - BigUint::one();

        if (&base).pow_mod(&num_1, num) != one() {
            Result::Composite
        } else {
            Result::PropablyPrime
        }
    }
}

pub struct MillerRabin<'a, T: Rng + 'a>(pub &'a mut T);

impl<'a, T: Rng + 'a> MillerRabin<'a, T> {
    fn greatest_2_divisor(num: &BigUint) -> (usize, BigUint) {
        let mut s = 0;
        let mut num = num - BigUint::one();
        while num.is_even() {
            num = num >> 1;
            s += 1;
        }

        (s, num)
    }

    fn witness(num: &BigUint, a: BigUint, d: &BigUint, s: usize) -> Result {
        let mut x = (&a).pow_mod(d, num);
        let num_1 = num - BigUint::one();

        if x == one() || x == num_1 { return Result::PropablyPrime }

        for _ in 0..s {
            x = (&x * &x) % num;
            if x == one() { return Result::Composite }
            if x == num_1 { return Result::PropablyPrime }
        }

        Result::Composite
    }
}

impl<'a, T: Rng + 'a> PrimeTest for MillerRabin<'a, T> {
    fn test(&mut self, num: &BigUint) -> Result {
        let a = self.0.next_u64().to_biguint().unwrap();
        let (s, d) = Self::greatest_2_divisor(&num);

        Self::witness(num, a, &d, s)
    }
}

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

    use rand::{StdRng, SeedableRng};
    use num::bigint::ToBigUint;

    const SEED: [usize; 1] = [0x00];

    fn rng() -> StdRng {
        StdRng::from_seed(&SEED[..])
    }

    #[test]
    fn test_fermat_prime() {
        let mut rng = rng();
        let mut fermat = Fermat(&mut rng);
        let res = fermat.test_loop(&4393139u64.to_biguint().unwrap(), 20);

        assert_eq!(res, Result::PropablyPrime);
    }

    #[test]
    fn test_fermat_composite() {
        let mut rng = rng();
        let mut fermat = Fermat(&mut rng);
        let res = fermat.test_loop(&4393137u64.to_biguint().unwrap(), 20);

        assert_eq!(res, Result::Composite);
    }

    #[test]
    fn test_miller_rabin_prime() {
        let mut rng = rng();
        let mut miller_rabin = MillerRabin(&mut rng);
        let res = miller_rabin.test_loop(&4393139u64.to_biguint().unwrap(), 20);

        assert_eq!(res, Result::PropablyPrime);
    }

    #[test]
    fn test_miller_rabin_composite() {
        let mut rng = rng();
        let mut miller_rabin = MillerRabin(&mut rng);
        let res = miller_rabin.test_loop(&4393137u64.to_biguint().unwrap(), 20);

        assert_eq!(res, Result::Composite);
    }
}
<commit_msg>tests(utils::primes::tests): cleanup tests structure<commit_after>use num::{one, One, Integer, BigUint};
use num::bigint::{ToBigUint};
use rand::Rng;

use utils::modular::power::Power;

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Type {
    PropablyPrime,
    Composite,
}

impl Type {
    pub fn is_composite(&self) -> bool {
        match *self {
            Type::Composite => true,
            _ => false
        }
    }
}

pub trait PrimeTest {
    fn test(&mut self, num: &BigUint) -> Type;

    fn test_loop(&mut self, num: &BigUint, times: usize) -> Type {
        for _ in 0..times {
            if self.test(&num).is_composite() { return Type::Composite }
        }

        Type::PropablyPrime
    }
}

pub struct Fermat<'a, T: Rng + 'a>(pub &'a mut T);

impl<'a, T: Rng + 'a> PrimeTest for Fermat<'a, T> {
    fn test(&mut self, num: &BigUint) -> Type {
        let base = self.0.next_u64().to_biguint().unwrap();
        let num_1 = num - BigUint::one();

        if (&base).pow_mod(&num_1, num) != one() {
            Type::Composite
        } else {
            Type::PropablyPrime
        }
    }
}

pub struct MillerRabin<'a, T: Rng + 'a>(pub &'a mut T);

impl<'a, T: Rng + 'a> MillerRabin<'a, T> {
    fn greatest_2_divisor(num: &BigUint) -> (usize, BigUint) {
        let mut s = 0;
        let mut num = num - BigUint::one();
        while num.is_even() {
            num = num >> 1;
            s += 1;
        }

        (s, num)
    }

    fn witness(num: &BigUint, a: BigUint, d: &BigUint, s: usize) -> Type {
        let mut x = (&a).pow_mod(d, num);
        let num_1 = num - BigUint::one();

        if x == one() || x == num_1 { return Type::PropablyPrime }

        for _ in 0..s {
            x = (&x * &x) % num;
            if x == one() { return Type::Composite }
            if x == num_1 { return Type::PropablyPrime }
        }

        Type::Composite
    }
}

impl<'a, T: Rng + 'a> PrimeTest for MillerRabin<'a, T> {
    fn test(&mut self, num: &BigUint) -> Type {
        let a = self.0.next_u64().to_biguint().unwrap();
        let (s, d) = Self::greatest_2_divisor(&num);

        Self::witness(num, a, &d, s)
    }
}

#[cfg(test)]
mod tests {
    use rand::{StdRng, SeedableRng};

    const SEED: [usize; 1] = [0x00];

    fn rng() -> StdRng {
        StdRng::from_seed(&SEED[..])
    }

    mod fermat {
        use num::bigint::ToBigUint;

        use super::rng;
        use super::super::{PrimeTest, Type, Fermat};

        #[test]
        fn prime() {
            let mut rng = rng();
            let mut fermat = Fermat(&mut rng);
            let res = fermat.test_loop(&4393139u64.to_biguint().unwrap(), 20);

            assert_eq!(res, Type::PropablyPrime);
        }

        #[test]
        fn composite() {
            let mut rng = rng();
            let mut fermat = Fermat(&mut rng);
            let res = fermat.test_loop(&4393137u64.to_biguint().unwrap(), 20);

            assert_eq!(res, Type::Composite);
        }
    }

    mod miller_rabin {
        use num::bigint::ToBigUint;

        use super::rng;
        use super::super::{PrimeTest, Type, MillerRabin};

        #[test]
        fn prime() {
            let mut rng = rng();
            let mut miller_rabin = MillerRabin(&mut rng);
            let res = miller_rabin.test_loop(&4393139u64.to_biguint().unwrap(), 20);

            assert_eq!(res, Type::PropablyPrime);
        }

        #[test]
        fn composite() {
            let mut rng = rng();
            let mut miller_rabin = MillerRabin(&mut rng);
            let res = miller_rabin.test_loop(&4393137u64.to_biguint().unwrap(), 20);

            assert_eq!(res, Type::Composite);
        }
    }
}
<|endoftext|>


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

// spell-checker:ignore (ToDO) COMFOLLOW Chowner Passwd RFILE RFILE's derefer dgid duid

// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]

#[macro_use]
extern crate uucore;
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path;
use uucore::libc::{gid_t, uid_t};
use uucore::perms::{wrap_chown, Verbosity};

use uucore::error::{UError, UResult, USimpleError};

use clap::{crate_version, App, Arg};

use walkdir::WalkDir;

use std::fs::{self, Metadata};
use std::os::unix::fs::MetadataExt;

use std::convert::AsRef;
use std::path::Path;
use uucore::InvalidEncodingHandling;

static ABOUT: &str = "change file owner and group";

pub mod options {
    pub mod verbosity {
        pub static CHANGES: &str = "changes";
        pub static QUIET: &str = "quiet";
        pub static SILENT: &str = "silent";
        pub static VERBOSE: &str = "verbose";
    }
    pub mod preserve_root {
        pub static PRESERVE: &str = "preserve-root";
        pub static NO_PRESERVE: &str = "no-preserve-root";
    }
    pub mod dereference {
        pub static DEREFERENCE: &str = "dereference";
        pub static NO_DEREFERENCE: &str = "no-dereference";
    }
    pub static FROM: &str = "from";
    pub static RECURSIVE: &str = "recursive";
    pub mod traverse {
        pub static TRAVERSE: &str = "H";
        pub static NO_TRAVERSE: &str = "P";
        pub static EVERY: &str = "L";
    }
    pub static REFERENCE: &str = "reference";
}

static ARG_OWNER: &str = "owner";
static ARG_FILES: &str = "files";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... [OWNER][:[GROUP]] FILE...\n{0} [OPTION]... --reference=RFILE FILE...",
        executable!()
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let usage = get_usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    /* First arg is the owner/group */
    let owner = matches.value_of(ARG_OWNER).unwrap();

    /* Then the list of files */
    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let preserve_root = matches.is_present(options::preserve_root::PRESERVE);

    let mut derefer = if matches.is_present(options::dereference::NO_DEREFERENCE) {
        Err(UError::from(1))
    } else {
        Ok(())
    };

    let mut bit_flag = if matches.is_present(options::traverse::TRAVERSE) {
        FTS_COMFOLLOW | FTS_PHYSICAL
    } else if matches.is_present(options::traverse::EVERY) {
        FTS_LOGICAL
    } else {
        FTS_PHYSICAL
    };

    let recursive = matches.is_present(options::RECURSIVE);
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer.is_err() {
                return Err(USimpleError::new(
                    1,
                    "-R --dereference requires -H or -L".to_string(),
                ));
            }
            derefer = Ok(());
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.is_present(options::verbosity::CHANGES) {
        Verbosity::Changes
    } else if matches.is_present(options::verbosity::SILENT)
        || matches.is_present(options::verbosity::QUIET)
    {
        Verbosity::Silent
    } else if matches.is_present(options::verbosity::VERBOSE) {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    let filter = if let Some(spec) = matches.value_of(options::FROM) {
        match parse_spec(spec) {
            Ok((Some(uid), None)) => IfFrom::User(uid),
            Ok((None, Some(gid))) => IfFrom::Group(gid),
            Ok((Some(uid), Some(gid))) => IfFrom::UserGroup(uid, gid),
            Ok((None, None)) => IfFrom::All,
            Err(e) => return Err(USimpleError::new(1, e)),
        }
    } else {
        IfFrom::All
    };

    let dest_uid: Option<u32>;
    let dest_gid: Option<u32>;
    if let Some(file) = matches.value_of(options::REFERENCE) {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = Some(meta.gid());
                dest_uid = Some(meta.uid());
            }
            Err(e) => {
                return Err(USimpleError::new(
                    1,
                    format!("failed to get attributes of '{}': {}", file, e),
                ));
            }
        }
    } else {
        match parse_spec(owner) {
            Ok((u, g)) => {
                dest_uid = u;
                dest_gid = g;
            }
            Err(e) => {
                return Err(USimpleError::new(1, e));
            }
        }
    }
    let executor = Chowner {
        bit_flag,
        dest_uid,
        dest_gid,
        verbosity,
        recursive,
        dereference: derefer.is_ok(),
        filter,
        preserve_root,
        files,
    };
    executor.exec()
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(options::verbosity::CHANGES)
                .short("c")
                .long(options::verbosity::CHANGES)
                .help("like verbose but report only when a change is made"),
        )
        .arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
            "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
        ))
        .arg(
            Arg::with_name(options::dereference::NO_DEREFERENCE)
                .short("h")
                .long(options::dereference::NO_DEREFERENCE)
                .help(
                    "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
                ),
        )
        .arg(
            Arg::with_name(options::FROM)
                .long(options::FROM)
                .help(
                    "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
                )
                .value_name("CURRENT_OWNER:CURRENT_GROUP"),
        )
        .arg(
            Arg::with_name(options::preserve_root::PRESERVE)
                .long(options::preserve_root::PRESERVE)
                .help("fail to operate recursively on '/'"),
        )
        .arg(
            Arg::with_name(options::preserve_root::NO_PRESERVE)
                .long(options::preserve_root::NO_PRESERVE)
                .help("do not treat '/' specially (the default)"),
        )
        .arg(
            Arg::with_name(options::verbosity::QUIET)
                .long(options::verbosity::QUIET)
                .help("suppress most error messages"),
        )
        .arg(
            Arg::with_name(options::RECURSIVE)
                .short("R")
                .long(options::RECURSIVE)
                .help("operate on files and directories recursively"),
        )
        .arg(
            Arg::with_name(options::REFERENCE)
                .long(options::REFERENCE)
                .help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
                .value_name("RFILE")
                .min_values(1),
        )
        .arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
        .arg(
            Arg::with_name(options::traverse::TRAVERSE)
                .short(options::traverse::TRAVERSE)
                .help("if a command line argument is a symbolic link to a directory, traverse it")
                .overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
        )
        .arg(
            Arg::with_name(options::traverse::EVERY)
                .short(options::traverse::EVERY)
                .help("traverse every symbolic link to a directory encountered")
                .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
        )
        .arg(
            Arg::with_name(options::traverse::NO_TRAVERSE)
                .short(options::traverse::NO_TRAVERSE)
                .help("do not traverse any symbolic links (default)")
                .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
        )
        .arg(
            Arg::with_name(options::verbosity::VERBOSE)
                .long(options::verbosity::VERBOSE)
                .help("output a diagnostic for every file processed"),
        )
        .arg(
            Arg::with_name(ARG_OWNER)
                .multiple(false)
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name(ARG_FILES)
                .multiple(true)
                .takes_value(true)
                .required(true)
                .min_values(1),
        )
}

fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
    let args = spec.split_terminator(':').collect::<Vec<_>>();
    let usr_only = args.len() == 1 && !args[0].is_empty();
    let grp_only = args.len() == 2 && args[0].is_empty();
    let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();
    let uid = if usr_only || usr_grp {
        Some(
            Passwd::locate(args[0])
                .map_err(|_| format!("invalid user: '{}'", spec))?
                .uid(),
        )
    } else {
        None
    };
    let gid = if grp_only || usr_grp {
        Some(
            Group::locate(args[1])
                .map_err(|_| format!("invalid group: '{}'", spec))?
                .gid(),
        )
    } else {
        None
    };
    Ok((uid, gid))
}

enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

struct Chowner {
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    bit_flag: u8,
    verbosity: Verbosity,
    filter: IfFrom,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => {
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    };
}

impl Chowner {
    fn exec(&self) -> UResult<()> {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        if ret != 0 {
            return Err(UError::from(ret));
        }
        Ok(())
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_error!("it is dangerous to operate recursively on '/'");
                    show_error!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                follow_arg,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity != Verbosity::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_error!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                follow,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity != Verbosity::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_error!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_error!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}

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

    #[test]
    fn test_parse_spec() {
        assert_eq!(parse_spec(":"), Ok((None, None)));
        assert!(parse_spec("::")
            .err()
            .unwrap()
            .starts_with("invalid group: "));
    }
}
<commit_msg>chown: code cleanup<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) COMFOLLOW Chowner Passwd RFILE RFILE's derefer dgid duid

#[macro_use]
extern crate uucore;
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path;
use uucore::libc::{gid_t, uid_t};
use uucore::perms::{wrap_chown, Verbosity};

use uucore::error::{FromIo, UError, UResult, USimpleError};

use clap::{crate_version, App, Arg};

use walkdir::WalkDir;

use std::fs::{self, Metadata};
use std::os::unix::fs::MetadataExt;

use std::convert::AsRef;
use std::path::Path;
use uucore::InvalidEncodingHandling;

static ABOUT: &str = "change file owner and group";

pub mod options {
    pub mod verbosity {
        pub static CHANGES: &str = "changes";
        pub static QUIET: &str = "quiet";
        pub static SILENT: &str = "silent";
        pub static VERBOSE: &str = "verbose";
    }
    pub mod preserve_root {
        pub static PRESERVE: &str = "preserve-root";
        pub static NO_PRESERVE: &str = "no-preserve-root";
    }
    pub mod dereference {
        pub static DEREFERENCE: &str = "dereference";
        pub static NO_DEREFERENCE: &str = "no-dereference";
    }
    pub static FROM: &str = "from";
    pub static RECURSIVE: &str = "recursive";
    pub mod traverse {
        pub static TRAVERSE: &str = "H";
        pub static NO_TRAVERSE: &str = "P";
        pub static EVERY: &str = "L";
    }
    pub static REFERENCE: &str = "reference";
}

static ARG_OWNER: &str = "owner";
static ARG_FILES: &str = "files";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... [OWNER][:[GROUP]] FILE...\n{0} [OPTION]... --reference=RFILE FILE...",
        executable!()
    )
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let usage = get_usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    /* First arg is the owner/group */
    let owner = matches.value_of(ARG_OWNER).unwrap();

    /* Then the list of files */
    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let preserve_root = matches.is_present(options::preserve_root::PRESERVE);

    let mut derefer = if matches.is_present(options::dereference::NO_DEREFERENCE) {
        1
    } else {
        0
    };

    let mut bit_flag = if matches.is_present(options::traverse::TRAVERSE) {
        FTS_COMFOLLOW | FTS_PHYSICAL
    } else if matches.is_present(options::traverse::EVERY) {
        FTS_LOGICAL
    } else {
        FTS_PHYSICAL
    };

    let recursive = matches.is_present(options::RECURSIVE);
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                return Err(USimpleError::new(
                    1,
                    "-R --dereference requires -H or -L".to_string(),
                ));
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.is_present(options::verbosity::CHANGES) {
        Verbosity::Changes
    } else if matches.is_present(options::verbosity::SILENT)
        || matches.is_present(options::verbosity::QUIET)
    {
        Verbosity::Silent
    } else if matches.is_present(options::verbosity::VERBOSE) {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    let filter = if let Some(spec) = matches.value_of(options::FROM) {
        match parse_spec(spec)? {
            (Some(uid), None) => IfFrom::User(uid),
            (None, Some(gid)) => IfFrom::Group(gid),
            (Some(uid), Some(gid)) => IfFrom::UserGroup(uid, gid),
            (None, None) => IfFrom::All,
        }
    } else {
        IfFrom::All
    };

    let dest_uid: Option<u32>;
    let dest_gid: Option<u32>;
    if let Some(file) = matches.value_of(options::REFERENCE) {
        let meta = fs::metadata(&file)
            .map_err_context(|| format!("failed to get attributes of '{}'", file))?;
        dest_gid = Some(meta.gid());
        dest_uid = Some(meta.uid());
    } else {
        let (u, g) = parse_spec(owner)?;
        dest_uid = u;
        dest_gid = g;
    }
    let executor = Chowner {
        bit_flag,
        dest_uid,
        dest_gid,
        verbosity,
        recursive,
        dereference: derefer != 0,
        filter,
        preserve_root,
        files,
    };
    executor.exec()
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(options::verbosity::CHANGES)
                .short("c")
                .long(options::verbosity::CHANGES)
                .help("like verbose but report only when a change is made"),
        )
        .arg(Arg::with_name(options::dereference::DEREFERENCE).long(options::dereference::DEREFERENCE).help(
            "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
        ))
        .arg(
            Arg::with_name(options::dereference::NO_DEREFERENCE)
                .short("h")
                .long(options::dereference::NO_DEREFERENCE)
                .help(
                    "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
                ),
        )
        .arg(
            Arg::with_name(options::FROM)
                .long(options::FROM)
                .help(
                    "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
                )
                .value_name("CURRENT_OWNER:CURRENT_GROUP"),
        )
        .arg(
            Arg::with_name(options::preserve_root::PRESERVE)
                .long(options::preserve_root::PRESERVE)
                .help("fail to operate recursively on '/'"),
        )
        .arg(
            Arg::with_name(options::preserve_root::NO_PRESERVE)
                .long(options::preserve_root::NO_PRESERVE)
                .help("do not treat '/' specially (the default)"),
        )
        .arg(
            Arg::with_name(options::verbosity::QUIET)
                .long(options::verbosity::QUIET)
                .help("suppress most error messages"),
        )
        .arg(
            Arg::with_name(options::RECURSIVE)
                .short("R")
                .long(options::RECURSIVE)
                .help("operate on files and directories recursively"),
        )
        .arg(
            Arg::with_name(options::REFERENCE)
                .long(options::REFERENCE)
                .help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
                .value_name("RFILE")
                .min_values(1),
        )
        .arg(Arg::with_name(options::verbosity::SILENT).short("f").long(options::verbosity::SILENT))
        .arg(
            Arg::with_name(options::traverse::TRAVERSE)
                .short(options::traverse::TRAVERSE)
                .help("if a command line argument is a symbolic link to a directory, traverse it")
                .overrides_with_all(&[options::traverse::EVERY, options::traverse::NO_TRAVERSE]),
        )
        .arg(
            Arg::with_name(options::traverse::EVERY)
                .short(options::traverse::EVERY)
                .help("traverse every symbolic link to a directory encountered")
                .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::NO_TRAVERSE]),
        )
        .arg(
            Arg::with_name(options::traverse::NO_TRAVERSE)
                .short(options::traverse::NO_TRAVERSE)
                .help("do not traverse any symbolic links (default)")
                .overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
        )
        .arg(
            Arg::with_name(options::verbosity::VERBOSE)
                .long(options::verbosity::VERBOSE)
                .help("output a diagnostic for every file processed"),
        )
        .arg(
            Arg::with_name(ARG_OWNER)
                .multiple(false)
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name(ARG_FILES)
                .multiple(true)
                .takes_value(true)
                .required(true)
                .min_values(1),
        )
}

fn parse_spec(spec: &str) -> UResult<(Option<u32>, Option<u32>)> {
    let args = spec.split_terminator(':').collect::<Vec<_>>();
    let usr_only = args.len() == 1 && !args[0].is_empty();
    let grp_only = args.len() == 2 && args[0].is_empty();
    let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();
    let uid = if usr_only || usr_grp {
        Some(
            Passwd::locate(args[0])
                .map_err(|_| USimpleError::new(1, format!("invalid user: '{}'", spec)))?
                .uid(),
        )
    } else {
        None
    };
    let gid = if grp_only || usr_grp {
        Some(
            Group::locate(args[1])
                .map_err(|_| USimpleError::new(1, format!("invalid group: '{}'", spec)))?
                .gid(),
        )
    } else {
        None
    };
    Ok((uid, gid))
}

enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

struct Chowner {
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    bit_flag: u8,
    verbosity: Verbosity,
    filter: IfFrom,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => {
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    };
}

impl Chowner {
    fn exec(&self) -> UResult<()> {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        if ret != 0 {
            return Err(UError::from(ret));
        }
        Ok(())
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_error!("it is dangerous to operate recursively on '/'");
                    show_error!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                follow_arg,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity != Verbosity::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_error!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                follow,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity != Verbosity::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_error!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_error!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}

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

    #[test]
    fn test_parse_spec() {
        assert_eq!(parse_spec(":"), Ok((None, None)));
        assert!(parse_spec("::")
            .err()
            .unwrap()
            .starts_with("invalid group: "));
    }
}
<|endoftext|>


<commit_before>//! Output advertising capabilities
//!
//! This module provides a type helping you to handle the advertising
//! of your compositor's output and their capabilities to your client,
//! as well as mapping your clients output request to your physical
//! outputs.
//!
//! # How to use it
//!
//! You need to instantiate an [`Output`]
//! for each output global you want to advertise to clients.
//!
//! Just add it to your Display using the [`Output::new(..)`](Output::new)
//! method. You can use the returned [`Output`] to change
//! the properties of your output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients.
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use smithay::wayland::output::{Output, PhysicalProperties, Mode};
//! use wayland_server::protocol::wl_output;
//!
//! # let mut display = wayland_server::Display::new();
//! // Create the Output with given name and physical properties
//! let (output, _output_global) = Output::new(
//!     &mut display,      // the display
//!     "output-0".into(), // the name of this output,
//!     PhysicalProperties {
//!         size: (200, 150).into(),        // dimensions (width, height) in mm
//!         subpixel: wl_output::Subpixel::HorizontalRgb,  // subpixel information
//!         make: "Screens Inc".into(),     // make of the monitor
//!         model: "Monitor Ultra".into(),  // model of the monitor
//!     },
//!     None // insert a logger here
//! );
//! // Now you can configure it
//! output.change_current_state(
//!     Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
//!     Some(wl_output::Transform::Normal), // global screen transformation
//!     Some(1), // global screen scaling factor
//!     Some((0,0).into()) // output position
//! );
//! // set the preferred mode
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { size: (800, 600).into(), refresh: 60000 });
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
//! ```

pub mod xdg;

use std::{
    ops::Deref as _,
    sync::{Arc, Mutex},
};

use wayland_server::protocol::{
    wl_output::{Subpixel, Transform},
    wl_surface,
};
use wayland_server::{
    protocol::wl_output::{Mode as WMode, WlOutput},
    Client, Display, Filter, Global, Main, UserDataMap,
};

use slog::{info, o, trace, warn};

use crate::utils::{Logical, Physical, Point, Raw, Size};

use self::xdg::XdgOutput;

/// An output mode
///
/// A possible combination of dimensions and refresh rate for an output.
///
/// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Mode {
    /// The size of the mode, in pixels
    pub size: Size<i32, Physical>,
    /// The refresh rate in millihertz
    ///
    /// `1000` is one fps (frame per second), `2000` is 2 fps, etc...
    pub refresh: i32,
}

/// The physical properties of an output
#[derive(Debug, Clone)]
pub struct PhysicalProperties {
    /// The size of the monitor, in millimeters
    pub size: Size<i32, Raw>,
    /// The subpixel geometry
    pub subpixel: Subpixel,
    /// Textual representation of the make
    pub make: String,
    /// Textual representation of the model
    pub model: String,
}

#[derive(Debug)]
pub(crate) struct Inner {
    name: String,
    pub(crate) log: ::slog::Logger,
    instances: Vec<WlOutput>,
    physical: PhysicalProperties,
    location: Point<i32, Logical>,
    transform: Transform,
    scale: i32,
    modes: Vec<Mode>,
    current_mode: Option<Mode>,
    preferred_mode: Option<Mode>,

    xdg_output: Option<XdgOutput>,
}

type InnerType = Arc<(Mutex<Inner>, UserDataMap)>;

impl Inner {
    fn new_global(&mut self, output: WlOutput) {
        trace!(self.log, "New global instantiated.");

        if self.modes.is_empty() {
            warn!(self.log, "Output is used with no modes set"; "name" => &self.name);
        }
        if self.current_mode.is_none() {
            warn!(self.log, "Output is used with no current mod set"; "name" => &self.name);
        }
        if self.preferred_mode.is_none() {
            warn!(self.log, "Output is used with not preferred mode set"; "name" => &self.name);
        }

        self.send_geometry(&output);
        for &mode in &self.modes {
            let mut flags = WMode::empty();
            if Some(mode) == self.current_mode {
                flags |= WMode::Current;
            }
            if Some(mode) == self.preferred_mode {
                flags |= WMode::Preferred;
            }
            output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
        }
        if output.as_ref().version() >= 2 {
            output.scale(self.scale);
            output.done();
        }

        self.instances.push(output);
    }

    fn send_geometry(&self, output: &WlOutput) {
        output.geometry(
            self.location.x,
            self.location.y,
            self.physical.size.w,
            self.physical.size.h,
            self.physical.subpixel,
            self.physical.make.clone(),
            self.physical.model.clone(),
            self.transform,
        );
    }
}

/// An output as seen by the clients
///
/// This handle is stored in the event loop, and allows you to notify clients
/// about any change in the properties of this output.
#[derive(Debug, Clone)]
pub struct Output {
    pub(crate) inner: InnerType,
}

impl Output {
    /// Create a new output global with given name and physical properties
    ///
    /// The global is directly registered into the event loop, and this function
    /// returns the state token allowing you to access it, as well as the global handle,
    /// in case you wish to remove this global in the future.
    pub fn new<L>(
        display: &mut Display,
        name: String,
        physical: PhysicalProperties,
        logger: L,
    ) -> (Output, Global<WlOutput>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "output_handler"));

        info!(log, "Creating new wl_output"; "name" => &name);

        let inner = Arc::new((
            Mutex::new(Inner {
                name,
                log,
                instances: Vec::new(),
                physical,
                location: (0, 0).into(),
                transform: Transform::Normal,
                scale: 1,
                modes: Vec::new(),
                current_mode: None,
                preferred_mode: None,
                xdg_output: None,
            }),
            UserDataMap::default(),
        ));

        let output = Output { inner: inner.clone() };

        let global = display.create_global(
            3,
            Filter::new(move |(output, _version): (Main<WlOutput>, _), _, _| {
                output.assign_destructor(Filter::new(|output: WlOutput, _, _| {
                    let inner = output.as_ref().user_data().get::<InnerType>().unwrap();
                    inner
                        .0
                        .lock()
                        .unwrap()
                        .instances
                        .retain(|o| !o.as_ref().equals(output.as_ref()));
                }));
                output.as_ref().user_data().set_threadsafe({
                    let inner = inner.clone();
                    move || inner
                });
                inner.0.lock().unwrap().new_global(output.deref().clone());
            }),
        );

        (output, global)
    }

    /// Attempt to retrieve a [`Output`] from an existing resource
    pub fn from_resource(output: &WlOutput) -> Option<Output> {
        output
            .as_ref()
            .user_data()
            .get::<InnerType>()
            .cloned()
            .map(|inner| Output { inner })
    }

    /// Sets the preferred mode of this output
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    pub fn set_preferred(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.preferred_mode = Some(mode);
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Adds a mode to the list of known modes to this output
    pub fn add_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Returns the currently advertised mode of the output
    pub fn current_mode(&self) -> Option<Mode> {
        self.inner.0.lock().unwrap().current_mode
    }

    /// Returns the currently advertised transformation of the output
    pub fn current_transform(&self) -> Transform {
        self.inner.0.lock().unwrap().transform
    }

    /// Returns the currenly advertised scale of the output
    pub fn current_scale(&self) -> i32 {
        self.inner.0.lock().unwrap().scale
    }

    /// Returns the name of the output
    pub fn name(&self) -> String {
        self.inner.0.lock().unwrap().name.clone()
    }

    /// Returns the physical properties of the output
    pub fn physical_properties(&self) -> PhysicalProperties {
        self.inner.0.lock().unwrap().physical.clone()
    }

    /// Removes a mode from the list of known modes
    ///
    /// It will not de-advertise it from existing clients (the protocol does not
    /// allow it), but it won't be advertised to now clients from now on.
    pub fn delete_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.modes.retain(|&m| m != mode);
        if inner.current_mode == Some(mode) {
            inner.current_mode = None;
        }
        if inner.preferred_mode == Some(mode) {
            inner.preferred_mode = None;
        }
    }

    /// Change the current state of this output
    ///
    /// You can changed the current mode, transform status, location or scale of this output. Providing
    /// `None` to any of these field means that the value does not change.
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    ///
    /// By default, transform status is `Normal`, and scale is `1`.
    pub fn change_current_state(
        &self,
        new_mode: Option<Mode>,
        new_transform: Option<Transform>,
        new_scale: Option<i32>,
        new_location: Option<Point<i32, Logical>>,
    ) {
        let mut inner = self.inner.0.lock().unwrap();
        if let Some(mode) = new_mode {
            if inner.modes.iter().all(|&m| m != mode) {
                inner.modes.push(mode);
            }
            inner.current_mode = new_mode;
        }
        if let Some(transform) = new_transform {
            inner.transform = transform;
        }
        if let Some(scale) = new_scale {
            inner.scale = scale;
        }
        let mut flags = WMode::Current;
        if inner.preferred_mode == new_mode {
            flags |= WMode::Preferred;
        }
        if let Some(new_location) = new_location {
            inner.location = new_location;
        }

        // XdgOutput has to be updated before WlOutput
        // Because WlOutput::done() has to allways be called last
        if let Some(xdg_output) = inner.xdg_output.as_ref() {
            xdg_output.change_current_state(new_mode, new_scale, new_location);
        }

        for output in &inner.instances {
            if let Some(mode) = new_mode {
                output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
            }
            if new_transform.is_some() || new_location.is_some() {
                inner.send_geometry(output);
            }
            if let Some(scale) = new_scale {
                if output.as_ref().version() >= 2 {
                    output.scale(scale);
                }
            }
            if output.as_ref().version() >= 2 {
                output.done();
            }
        }
    }

    /// Check is given [`wl_output`](WlOutput) instance is managed by this [`Output`].
    pub fn owns(&self, output: &WlOutput) -> bool {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .any(|o| o.as_ref().equals(output.as_ref()))
    }

    /// This function allows to run a [FnMut] on every
    /// [WlOutput] matching the same [Client] as provided
    pub fn with_client_outputs<F>(&self, client: Client, f: F)
    where
        F: FnMut(&WlOutput),
    {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .filter(|output| match output.as_ref().client() {
                Some(output_client) => output_client.equals(&client),
                None => false,
            })
            .for_each(f)
    }

    /// Sends `wl_surface.enter` for the provided surface
    /// with the matching client output
    pub fn enter(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.enter(output))
        }
    }

    /// Sends `wl_surface.leave` for the provided surface
    /// with the matching client output
    pub fn leave(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.leave(output))
        }
    }

    /// Returns the user data of this output
    pub fn user_data(&self) -> &UserDataMap {
        &self.inner.1
    }
}

impl PartialEq for Output {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for Output {}
<commit_msg>output: Make it possible to query location<commit_after>//! Output advertising capabilities
//!
//! This module provides a type helping you to handle the advertising
//! of your compositor's output and their capabilities to your client,
//! as well as mapping your clients output request to your physical
//! outputs.
//!
//! # How to use it
//!
//! You need to instantiate an [`Output`]
//! for each output global you want to advertise to clients.
//!
//! Just add it to your Display using the [`Output::new(..)`](Output::new)
//! method. You can use the returned [`Output`] to change
//! the properties of your output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients.
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use smithay::wayland::output::{Output, PhysicalProperties, Mode};
//! use wayland_server::protocol::wl_output;
//!
//! # let mut display = wayland_server::Display::new();
//! // Create the Output with given name and physical properties
//! let (output, _output_global) = Output::new(
//!     &mut display,      // the display
//!     "output-0".into(), // the name of this output,
//!     PhysicalProperties {
//!         size: (200, 150).into(),        // dimensions (width, height) in mm
//!         subpixel: wl_output::Subpixel::HorizontalRgb,  // subpixel information
//!         make: "Screens Inc".into(),     // make of the monitor
//!         model: "Monitor Ultra".into(),  // model of the monitor
//!     },
//!     None // insert a logger here
//! );
//! // Now you can configure it
//! output.change_current_state(
//!     Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
//!     Some(wl_output::Transform::Normal), // global screen transformation
//!     Some(1), // global screen scaling factor
//!     Some((0,0).into()) // output position
//! );
//! // set the preferred mode
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { size: (800, 600).into(), refresh: 60000 });
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
//! ```

pub mod xdg;

use std::{
    ops::Deref as _,
    sync::{Arc, Mutex},
};

use wayland_server::protocol::{
    wl_output::{Subpixel, Transform},
    wl_surface,
};
use wayland_server::{
    protocol::wl_output::{Mode as WMode, WlOutput},
    Client, Display, Filter, Global, Main, UserDataMap,
};

use slog::{info, o, trace, warn};

use crate::utils::{Logical, Physical, Point, Raw, Size};

use self::xdg::XdgOutput;

/// An output mode
///
/// A possible combination of dimensions and refresh rate for an output.
///
/// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Mode {
    /// The size of the mode, in pixels
    pub size: Size<i32, Physical>,
    /// The refresh rate in millihertz
    ///
    /// `1000` is one fps (frame per second), `2000` is 2 fps, etc...
    pub refresh: i32,
}

/// The physical properties of an output
#[derive(Debug, Clone)]
pub struct PhysicalProperties {
    /// The size of the monitor, in millimeters
    pub size: Size<i32, Raw>,
    /// The subpixel geometry
    pub subpixel: Subpixel,
    /// Textual representation of the make
    pub make: String,
    /// Textual representation of the model
    pub model: String,
}

#[derive(Debug)]
pub(crate) struct Inner {
    name: String,
    pub(crate) log: ::slog::Logger,
    instances: Vec<WlOutput>,
    physical: PhysicalProperties,
    location: Point<i32, Logical>,
    transform: Transform,
    scale: i32,
    modes: Vec<Mode>,
    current_mode: Option<Mode>,
    preferred_mode: Option<Mode>,

    xdg_output: Option<XdgOutput>,
}

type InnerType = Arc<(Mutex<Inner>, UserDataMap)>;

impl Inner {
    fn new_global(&mut self, output: WlOutput) {
        trace!(self.log, "New global instantiated.");

        if self.modes.is_empty() {
            warn!(self.log, "Output is used with no modes set"; "name" => &self.name);
        }
        if self.current_mode.is_none() {
            warn!(self.log, "Output is used with no current mod set"; "name" => &self.name);
        }
        if self.preferred_mode.is_none() {
            warn!(self.log, "Output is used with not preferred mode set"; "name" => &self.name);
        }

        self.send_geometry(&output);
        for &mode in &self.modes {
            let mut flags = WMode::empty();
            if Some(mode) == self.current_mode {
                flags |= WMode::Current;
            }
            if Some(mode) == self.preferred_mode {
                flags |= WMode::Preferred;
            }
            output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
        }
        if output.as_ref().version() >= 2 {
            output.scale(self.scale);
            output.done();
        }

        self.instances.push(output);
    }

    fn send_geometry(&self, output: &WlOutput) {
        output.geometry(
            self.location.x,
            self.location.y,
            self.physical.size.w,
            self.physical.size.h,
            self.physical.subpixel,
            self.physical.make.clone(),
            self.physical.model.clone(),
            self.transform,
        );
    }
}

/// An output as seen by the clients
///
/// This handle is stored in the event loop, and allows you to notify clients
/// about any change in the properties of this output.
#[derive(Debug, Clone)]
pub struct Output {
    pub(crate) inner: InnerType,
}

impl Output {
    /// Create a new output global with given name and physical properties
    ///
    /// The global is directly registered into the event loop, and this function
    /// returns the state token allowing you to access it, as well as the global handle,
    /// in case you wish to remove this global in the future.
    pub fn new<L>(
        display: &mut Display,
        name: String,
        physical: PhysicalProperties,
        logger: L,
    ) -> (Output, Global<WlOutput>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "output_handler"));

        info!(log, "Creating new wl_output"; "name" => &name);

        let inner = Arc::new((
            Mutex::new(Inner {
                name,
                log,
                instances: Vec::new(),
                physical,
                location: (0, 0).into(),
                transform: Transform::Normal,
                scale: 1,
                modes: Vec::new(),
                current_mode: None,
                preferred_mode: None,
                xdg_output: None,
            }),
            UserDataMap::default(),
        ));

        let output = Output { inner: inner.clone() };

        let global = display.create_global(
            3,
            Filter::new(move |(output, _version): (Main<WlOutput>, _), _, _| {
                output.assign_destructor(Filter::new(|output: WlOutput, _, _| {
                    let inner = output.as_ref().user_data().get::<InnerType>().unwrap();
                    inner
                        .0
                        .lock()
                        .unwrap()
                        .instances
                        .retain(|o| !o.as_ref().equals(output.as_ref()));
                }));
                output.as_ref().user_data().set_threadsafe({
                    let inner = inner.clone();
                    move || inner
                });
                inner.0.lock().unwrap().new_global(output.deref().clone());
            }),
        );

        (output, global)
    }

    /// Attempt to retrieve a [`Output`] from an existing resource
    pub fn from_resource(output: &WlOutput) -> Option<Output> {
        output
            .as_ref()
            .user_data()
            .get::<InnerType>()
            .cloned()
            .map(|inner| Output { inner })
    }

    /// Sets the preferred mode of this output
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    pub fn set_preferred(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.preferred_mode = Some(mode);
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Adds a mode to the list of known modes to this output
    pub fn add_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Returns the currently advertised mode of the output
    pub fn current_mode(&self) -> Option<Mode> {
        self.inner.0.lock().unwrap().current_mode
    }

    /// Returns the currently advertised transformation of the output
    pub fn current_transform(&self) -> Transform {
        self.inner.0.lock().unwrap().transform
    }

    /// Returns the currenly advertised scale of the output
    pub fn current_scale(&self) -> i32 {
        self.inner.0.lock().unwrap().scale
    }

    /// Returns the currenly advertised location of the output
    pub fn current_location(&self) -> Point<i32, Logical> {
        self.inner.0.lock().unwrap().location
    }

    /// Returns the name of the output
    pub fn name(&self) -> String {
        self.inner.0.lock().unwrap().name.clone()
    }

    /// Returns the physical properties of the output
    pub fn physical_properties(&self) -> PhysicalProperties {
        self.inner.0.lock().unwrap().physical.clone()
    }

    /// Removes a mode from the list of known modes
    ///
    /// It will not de-advertise it from existing clients (the protocol does not
    /// allow it), but it won't be advertised to now clients from now on.
    pub fn delete_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.modes.retain(|&m| m != mode);
        if inner.current_mode == Some(mode) {
            inner.current_mode = None;
        }
        if inner.preferred_mode == Some(mode) {
            inner.preferred_mode = None;
        }
    }

    /// Change the current state of this output
    ///
    /// You can changed the current mode, transform status, location or scale of this output. Providing
    /// `None` to any of these field means that the value does not change.
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    ///
    /// By default, transform status is `Normal`, and scale is `1`.
    pub fn change_current_state(
        &self,
        new_mode: Option<Mode>,
        new_transform: Option<Transform>,
        new_scale: Option<i32>,
        new_location: Option<Point<i32, Logical>>,
    ) {
        let mut inner = self.inner.0.lock().unwrap();
        if let Some(mode) = new_mode {
            if inner.modes.iter().all(|&m| m != mode) {
                inner.modes.push(mode);
            }
            inner.current_mode = new_mode;
        }
        if let Some(transform) = new_transform {
            inner.transform = transform;
        }
        if let Some(scale) = new_scale {
            inner.scale = scale;
        }
        let mut flags = WMode::Current;
        if inner.preferred_mode == new_mode {
            flags |= WMode::Preferred;
        }
        if let Some(new_location) = new_location {
            inner.location = new_location;
        }

        // XdgOutput has to be updated before WlOutput
        // Because WlOutput::done() has to allways be called last
        if let Some(xdg_output) = inner.xdg_output.as_ref() {
            xdg_output.change_current_state(new_mode, new_scale, new_location);
        }

        for output in &inner.instances {
            if let Some(mode) = new_mode {
                output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
            }
            if new_transform.is_some() || new_location.is_some() {
                inner.send_geometry(output);
            }
            if let Some(scale) = new_scale {
                if output.as_ref().version() >= 2 {
                    output.scale(scale);
                }
            }
            if output.as_ref().version() >= 2 {
                output.done();
            }
        }
    }

    /// Check is given [`wl_output`](WlOutput) instance is managed by this [`Output`].
    pub fn owns(&self, output: &WlOutput) -> bool {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .any(|o| o.as_ref().equals(output.as_ref()))
    }

    /// This function allows to run a [FnMut] on every
    /// [WlOutput] matching the same [Client] as provided
    pub fn with_client_outputs<F>(&self, client: Client, f: F)
    where
        F: FnMut(&WlOutput),
    {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .filter(|output| match output.as_ref().client() {
                Some(output_client) => output_client.equals(&client),
                None => false,
            })
            .for_each(f)
    }

    /// Sends `wl_surface.enter` for the provided surface
    /// with the matching client output
    pub fn enter(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.enter(output))
        }
    }

    /// Sends `wl_surface.leave` for the provided surface
    /// with the matching client output
    pub fn leave(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.leave(output))
        }
    }

    /// Returns the user data of this output
    pub fn user_data(&self) -> &UserDataMap {
        &self.inner.1
    }
}

impl PartialEq for Output {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for Output {}
<|endoftext|>