<commit_before>// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Common protocol bits.

#![allow(dead_code)] // various things are unused with --no-default-features
#![allow(missing_docs)]

use std::collections::HashMap;
use std::str::FromStr;

use eui48::MacAddress;
use reqwest::{Method, Url};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::{DeserializeOwned, Error as DeserError};
use serde_json;

use super::super::{Error, ErrorKind, Result};
use super::super::auth::AuthMethod;
use super::super::session::{RequestBuilderExt, ServiceType};
use super::super::utils;
use super::ApiVersion;

#[derive(Clone, Debug, Deserialize)]
pub struct Link {
    #[serde(deserialize_with = "deser_url")]
    pub href: Url,
    pub rel: String
}

#[derive(Clone, Debug, Deserialize)]
pub struct Ref {
    pub id: String,
    pub links: Vec<Link>
}

#[derive(Clone, Debug, Deserialize)]
pub struct IdAndName {
    pub id: String,
    pub name: String
}

#[derive(Clone, Debug, Deserialize)]
pub struct KeyValue {
    pub key: String,
    pub value: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct Version {
    #[serde(deserialize_with = "deser_version")]
    pub id: ApiVersion,
    pub links: Vec<Link>,
    pub status: String,
    #[serde(deserialize_with = "empty_as_none", default)]
    pub version: Option<ApiVersion>,
    #[serde(deserialize_with = "empty_as_none", default)]
    pub min_version: Option<ApiVersion>
}

#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum Root {
    Versions { versions: Vec<Version> },
    Version { version: Version }
}

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Major API version.
    pub major_version: ApiVersion,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

impl Version {
    pub fn into_service_info(self) -> Result<ServiceInfo> {
        let endpoint = match self.links.into_iter().find(|x| &x.rel == "self") {
            Some(link) => link.href,
            None => {
                return Err(Error::new(
                    ErrorKind::InvalidResponse,
                    "Invalid version - missing self link"));
            }
        };

        Ok(ServiceInfo {
            root_url: endpoint,
            major_version: self.id,
            current_version: self.version,
            minimum_version: self.min_version
        })
    }
}

impl ServiceInfo {
    /// Generic code to extract a `ServiceInfo` from a URL.
    pub fn fetch<Srv: ServiceType>(endpoint: Url, auth: &AuthMethod) -> Result<ServiceInfo> {
        let service_type = Srv::catalog_type();
        debug!("Fetching {} service info from {}", service_type, endpoint);

        // Workaround for old version of Nova returning HTTP endpoints even if
        // accessed via HTTP
        let secure = endpoint.scheme() == "https";

        let result = auth.request(Method::GET, endpoint.clone())?.send_checked();
        match result {
            Ok(mut resp) => {
                let mut info = match resp.json()? {
                    Root::Version { version: ver } => {
                        trace!("The major version for {} service from {}: {:?}",
                               service_type, endpoint, ver);
                        if Srv::major_version_supported(ver.id) {
                            ver.into_service_info()
                        } else {
                            Err(Error::new(ErrorKind::EndpointNotFound,
                                           "Major version not supported"))
                        }
                    },
                    Root::Versions { versions: mut vers } => {
                        vers.sort_unstable_by_key(|x| x.id);
                        trace!("Available major versions for {} service from {}: {:?}",
                               service_type, endpoint, vers);
                        match vers.into_iter()
                                .rfind(|x| Srv::major_version_supported(x.id)) {
                            Some(ver) => ver.into_service_info(),
                            None => Err(Error::new_endpoint_not_found(service_type))
                        }
                    }
                }?;

                // Older Nova returns insecure URLs even for secure protocol.
                if secure {
                    info.root_url.set_scheme("https").unwrap();
                }

                debug!("Received {:?} for {} service from {}",
                       info, service_type, endpoint);
                Ok(info)
            },
            Err(ref e) if e.kind() == ErrorKind::ResourceNotFound => {
                if utils::url::is_root(&endpoint) {
                    Err(Error::new_endpoint_not_found(service_type))
                } else {
                    debug!("Got HTTP 404 from {}, trying parent endpoint",
                           endpoint);
                    ServiceInfo::fetch::<Srv>(utils::url::pop(endpoint, true),
                                              auth)
                }
            },
            Err(other) => Err(other)
        }
    }
}

/// Deserialize value where empty string equals None.
pub fn empty_as_none<'de, D, T>(des: D) -> ::std::result::Result<Option<T>, D::Error>
        where D: Deserializer<'de>, T: DeserializeOwned {
    let value = serde_json::Value::deserialize(des)?;
    match value {
        serde_json::Value::String(ref s) if s == "" => return Ok(None),
        _ => ()
    };

    serde_json::from_value(value).map_err(DeserError::custom)
}

/// Deserialize value where empty string equals None.
pub fn empty_as_default<'de, D, T>(des: D) -> ::std::result::Result<T, D::Error>
        where D: Deserializer<'de>, T: DeserializeOwned + Default {
    let value = serde_json::Value::deserialize(des)?;
    match value {
        serde_json::Value::String(ref s) if s == "" =>
            return Ok(Default::default()),
        _ => ()
    };

    serde_json::from_value(value).map_err(DeserError::custom)
}

pub fn deser_version<'de, D>(des: D)
        -> ::std::result::Result<ApiVersion, D::Error>
        where D: Deserializer<'de> {
    let value = String::deserialize(des)?;
    if value.is_empty() {
        return Err(D::Error::custom("Empty version ID"));
    }

    let version_part = if value.starts_with("v") {
        &value[1..]
    } else {
        &value
    };

    ApiVersion::from_str(version_part).map_err(D::Error::custom)
}

/// Deserialize a URL.
pub fn deser_url<'de, D>(des: D) -> ::std::result::Result<Url, D::Error>
        where D: Deserializer<'de> {
    Url::parse(&String::deserialize(des)?).map_err(DeserError::custom)
}

/// Deserialize a URL.
pub fn deser_optional_url<'de, D>(des: D)
        -> ::std::result::Result<Option<Url>, D::Error>
        where D: Deserializer<'de> {
    let value: Option<String> = Deserialize::deserialize(des)?;
    match value {
        Some(s) => Url::parse(&s).map_err(DeserError::custom).map(Some),
        None => Ok(None)
    }
}

/// Deserialize a key-value mapping.
pub fn deser_key_value<'de, D>(des: D)
        -> ::std::result::Result<HashMap<String, String>, D::Error>
        where D: Deserializer<'de> {
    let value: Vec<KeyValue> = Deserialize::deserialize(des)?;
    Ok(value.into_iter().map(|kv| (kv.key, kv.value)).collect())
}

/// Serialize a MAC address in its HEX format.
pub fn ser_mac<S>(value: &MacAddress, serializer: S)
        -> ::std::result::Result<S::Ok, S::Error>
        where S: Serializer {
    value.to_hex_string().serialize(serializer)
}

/// Serialize a MAC address in its HEX format.
pub fn ser_opt_mac<S>(value: &Option<MacAddress>, serializer: S)
        -> ::std::result::Result<S::Ok, S::Error>
        where S: Serializer {
    value.map(|m| m.to_hex_string()).serialize(serializer)
}
<commit_msg>fix(common): only consider stable major versions<commit_after>// Copyright 2018 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Common protocol bits.

#![allow(dead_code)] // various things are unused with --no-default-features
#![allow(missing_docs)]

use std::collections::HashMap;
use std::str::FromStr;

use eui48::MacAddress;
use reqwest::{Method, Url};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::{DeserializeOwned, Error as DeserError};
use serde_json;

use super::super::{Error, ErrorKind, Result};
use super::super::auth::AuthMethod;
use super::super::session::{RequestBuilderExt, ServiceType};
use super::super::utils;
use super::ApiVersion;

#[derive(Clone, Debug, Deserialize)]
pub struct Link {
    #[serde(deserialize_with = "deser_url")]
    pub href: Url,
    pub rel: String
}

#[derive(Clone, Debug, Deserialize)]
pub struct Ref {
    pub id: String,
    pub links: Vec<Link>
}

#[derive(Clone, Debug, Deserialize)]
pub struct IdAndName {
    pub id: String,
    pub name: String
}

#[derive(Clone, Debug, Deserialize)]
pub struct KeyValue {
    pub key: String,
    pub value: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct Version {
    #[serde(deserialize_with = "deser_version")]
    pub id: ApiVersion,
    pub links: Vec<Link>,
    #[serde(deserialize_with = "empty_as_none", default)]
    pub status: Option<String>,
    #[serde(deserialize_with = "empty_as_none", default)]
    pub version: Option<ApiVersion>,
    #[serde(deserialize_with = "empty_as_none", default)]
    pub min_version: Option<ApiVersion>
}

#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum Root {
    Versions { versions: Vec<Version> },
    Version { version: Version }
}

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Major API version.
    pub major_version: ApiVersion,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

impl Version {
    pub fn is_stable(&self) -> bool {
        if let Some(ref status) = self.status {
            let upper = status.to_uppercase();
            upper == "STABLE" || upper == "CURRENT"
        } else {
            true
        }
    }

    pub fn into_service_info(self) -> Result<ServiceInfo> {
        let endpoint = match self.links.into_iter().find(|x| &x.rel == "self") {
            Some(link) => link.href,
            None => {
                return Err(Error::new(
                    ErrorKind::InvalidResponse,
                    "Invalid version - missing self link"));
            }
        };

        Ok(ServiceInfo {
            root_url: endpoint,
            major_version: self.id,
            current_version: self.version,
            minimum_version: self.min_version
        })
    }
}

impl ServiceInfo {
    /// Generic code to extract a `ServiceInfo` from a URL.
    pub fn fetch<Srv: ServiceType>(endpoint: Url, auth: &AuthMethod) -> Result<ServiceInfo> {
        let service_type = Srv::catalog_type();
        debug!("Fetching {} service info from {}", service_type, endpoint);

        // Workaround for old version of Nova returning HTTP endpoints even if
        // accessed via HTTP
        let secure = endpoint.scheme() == "https";

        let result = auth.request(Method::GET, endpoint.clone())?.send_checked();
        match result {
            Ok(mut resp) => {
                let mut info = match resp.json()? {
                    Root::Version { version: ver } => {
                        trace!("The major version for {} service from {}: {:?}",
                               service_type, endpoint, ver);
                        if Srv::major_version_supported(ver.id) {
                            ver.into_service_info()
                        } else {
                            Err(Error::new(ErrorKind::EndpointNotFound,
                                           "Major version not supported"))
                        }
                    },
                    Root::Versions { versions: mut vers } => {
                        vers.sort_unstable_by_key(|x| x.id);
                        trace!("Available major versions for {} service from {}: {:?}",
                               service_type, endpoint, vers);
                        match vers.into_iter()
                                .filter(Version::is_stable)
                                .rfind(|x| Srv::major_version_supported(x.id)) {
                            Some(ver) => ver.into_service_info(),
                            None => Err(Error::new_endpoint_not_found(service_type))
                        }
                    }
                }?;

                // Older Nova returns insecure URLs even for secure protocol.
                if secure {
                    info.root_url.set_scheme("https").unwrap();
                }

                debug!("Received {:?} for {} service from {}",
                       info, service_type, endpoint);
                Ok(info)
            },
            Err(ref e) if e.kind() == ErrorKind::ResourceNotFound => {
                if utils::url::is_root(&endpoint) {
                    Err(Error::new_endpoint_not_found(service_type))
                } else {
                    debug!("Got HTTP 404 from {}, trying parent endpoint",
                           endpoint);
                    ServiceInfo::fetch::<Srv>(utils::url::pop(endpoint, true),
                                              auth)
                }
            },
            Err(other) => Err(other)
        }
    }
}

/// Deserialize value where empty string equals None.
pub fn empty_as_none<'de, D, T>(des: D) -> ::std::result::Result<Option<T>, D::Error>
        where D: Deserializer<'de>, T: DeserializeOwned {
    let value = serde_json::Value::deserialize(des)?;
    match value {
        serde_json::Value::String(ref s) if s == "" => return Ok(None),
        _ => ()
    };

    serde_json::from_value(value).map_err(DeserError::custom)
}

/// Deserialize value where empty string equals None.
pub fn empty_as_default<'de, D, T>(des: D) -> ::std::result::Result<T, D::Error>
        where D: Deserializer<'de>, T: DeserializeOwned + Default {
    let value = serde_json::Value::deserialize(des)?;
    match value {
        serde_json::Value::String(ref s) if s == "" =>
            return Ok(Default::default()),
        _ => ()
    };

    serde_json::from_value(value).map_err(DeserError::custom)
}

pub fn deser_version<'de, D>(des: D)
        -> ::std::result::Result<ApiVersion, D::Error>
        where D: Deserializer<'de> {
    let value = String::deserialize(des)?;
    if value.is_empty() {
        return Err(D::Error::custom("Empty version ID"));
    }

    let version_part = if value.starts_with("v") {
        &value[1..]
    } else {
        &value
    };

    ApiVersion::from_str(version_part).map_err(D::Error::custom)
}

/// Deserialize a URL.
pub fn deser_url<'de, D>(des: D) -> ::std::result::Result<Url, D::Error>
        where D: Deserializer<'de> {
    Url::parse(&String::deserialize(des)?).map_err(DeserError::custom)
}

/// Deserialize a URL.
pub fn deser_optional_url<'de, D>(des: D)
        -> ::std::result::Result<Option<Url>, D::Error>
        where D: Deserializer<'de> {
    let value: Option<String> = Deserialize::deserialize(des)?;
    match value {
        Some(s) => Url::parse(&s).map_err(DeserError::custom).map(Some),
        None => Ok(None)
    }
}

/// Deserialize a key-value mapping.
pub fn deser_key_value<'de, D>(des: D)
        -> ::std::result::Result<HashMap<String, String>, D::Error>
        where D: Deserializer<'de> {
    let value: Vec<KeyValue> = Deserialize::deserialize(des)?;
    Ok(value.into_iter().map(|kv| (kv.key, kv.value)).collect())
}

/// Serialize a MAC address in its HEX format.
pub fn ser_mac<S>(value: &MacAddress, serializer: S)
        -> ::std::result::Result<S::Ok, S::Error>
        where S: Serializer {
    value.to_hex_string().serialize(serializer)
}

/// Serialize a MAC address in its HEX format.
pub fn ser_opt_mac<S>(value: &Option<MacAddress>, serializer: S)
        -> ::std::result::Result<S::Ok, S::Error>
        where S: Serializer {
    value.map(|m| m.to_hex_string()).serialize(serializer)
}
<|endoftext|>


<commit_before>//! sock
//!
//! Sock is a websocket server.
//! Sister server to `web`, which is the static file server.
use ws;
use time;
use std::fmt;
use uuid::Uuid;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::{HashMap};

mod msg;
mod cookie;
mod authorizer;

use self::cookie::{parse_cookies,put_cookie};
use game::{Action,Player,AnonymousPlayer};
use self::authorizer::{AuthorizesTicket,DumbTicketStamper};
use self::msg::{ActionMsg,parse};

#[derive(Debug, Clone)]
pub struct Connection {
    token  : ws::util::Token,
    ticket : Uuid,
    player : Player,
}

impl Connection {
    fn new(tk: ws::util::Token, tx: Uuid, p: Player) -> Connection {
        Connection {
            token : tk,
            ticket: tx,
            player: p,
        }
    }
}

impl fmt::Display for Connection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "conn[{:?}]{{{}}}", self.token, self.ticket)
    }
}

fn ws_log (evt: &str, token: ws::util::Token, msg: &str) {
    println!("ws:{}[{}][{:?}]: {}", evt, time::precise_time_ns(), token, msg)
}

fn ws_conn_log (evt: &str, con: &Connection, msg: &str) {
    println!("ws:{}[{}]:[{}]: {}", evt, time::precise_time_ns(), con, msg)
}

pub type ConnectionMap = Rc<RefCell<HashMap<ws::util::Token, Rc<RefCell<Connection>>>>>;

struct WSServer<T> {
    out: ws::Sender,
    connections: ConnectionMap,
    authorizer: T,
}

impl ws::Handler for WSServer<DumbTicketStamper> {
    fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
        let token = self.out.token();
        ws_log("req", token, "on_request received");

        let cookies: HashMap<String, String> = parse_cookies(req);
        let ticket: Option<Uuid> = cookies
            .get("bzwf_anon_wstx")
            .and_then(|uuid_string| Uuid::parse_str(uuid_string.as_str()).ok());

        let mut resp = ws::Response::from_request(req).unwrap();

        if let Some(ticket) = ticket {
            // If you have a ticket, you may have authenticated already.
            // Your connection was already established and has been upgraded.
            let conn_map = self.connections.borrow();
            let conn = conn_map.get(&token).unwrap().borrow();
            // TODO(jordan): ???
            let _ = self.authorizer.authorize_ticket(token, ticket)
                .map(|_| {
                    ws_conn_log("req", &conn, "ticket matches connection ticket");
                })
                .map_err(|err| {
                    ws_conn_log("req", &conn, &format!("err {}", err));
                });
        } else {
            // QUESTION(jordan): does lack of a ticket always mean anonymous player?
            // You do not have a ticket. Create a new Connection and authenticate anonymously.
            let ticket = Uuid::new_v4();
            let new_conn = Connection::new(token, ticket, AnonymousPlayer::new());
            ws_conn_log("req", &new_conn, "new connection");
            ws_conn_log("req", &new_conn, &format!("put cookie bzwf_anon_wstx={}", ticket));
            put_cookie(String::from("bzwf_anon_wstx"), ticket.to_string(), &mut resp);
            self.connections.borrow_mut().insert(token, Rc::new(RefCell::new(new_conn)));
        }

        ws_log("req", token, &format!("{} connected users", self.connections.borrow().len()));

        Ok(resp)
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        let token = self.out.token();

        let _ = parse(msg)
            .and_then(|msg_cell: ActionMsg| {
                let action: Action = msg_cell.val.clone();
                let ticket: Uuid = msg_cell.next.val;

                self.authorizer.authorize_ticket(token, ticket)
                    .map(|conn| (conn, action))
            })
            .map(|(conn, action)| {
                let Action::addItemToInventory(item) = action.clone();
                conn.borrow_mut().player.state().inventory.insert(item);
                ws_conn_log("rcv", &conn.borrow(), &format!("{:?}", action));
                println!("{:?}", conn.borrow_mut().player.state());
                let _ = self.out.send(format!("{:?}", conn.borrow_mut().player.state()));
            })
            .map_err(|err: String| {
                ws_log("rcv", token, &format!("err {}", err));
                let _ = self.out.send(err);
            });

        Ok(())
    }

    fn on_open(&mut self, hs: ws::Handshake) -> ws::Result<()> {
        // NOTE(jordan): There should be a Connection in the map, and a cookie matching the ticket.
        let token   = self.out.token();
        let cookies = parse_cookies(&hs.request);

        let _ = cookies.get("bzwf_anon_wstx")
            .ok_or(String::from("No bzwf_anon_wstx cookie!"))
            // ^^ TODO?(jordan): ws::Error?
            .and_then(|ticket| Uuid::parse_str(ticket).map_err(|_| String::from("invalid cookie")))
            .map(|ticket| (token, ticket))
            .and_then(|(token, ticket)| self.authorizer.authorize_ticket(token, ticket))
            .map(|conn| ws_conn_log("opn", &conn.borrow(), "cxn opened"))
            .map_err(|err| ws_log("opn", token, &format!("error: {}", err)));

        Ok(())
    }

    fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
        // TODO(jordan): remove a user's Connection struct
        let token = self.out.token();
        self.connections.borrow()
            .get(&token)
            .map(|conn| {
                ws_conn_log("cls", &conn.borrow(), &format!("cxn closed\n\tcode [{:?}] reason: {}", code, reason));
            });

        self.connections
            .borrow_mut()
            .remove(&token);

        ws_log("cls", token, &format!("{} users connected", self.connections.borrow().len()));
    }

    fn on_error(&mut self, err: ws::Error) {
        let token = self.out.token();
        // NOTE(jordan): this happens when we return a ws::Error. Handle it individually. match?
        ws_log("err", token, &format!("got error {}", err));
    }
}

pub fn server (domain: String, port: i32) -> () {
    let connections: ConnectionMap = Rc::new(RefCell::new(HashMap::new()));
    let addr: String = format!("{}:{}", domain, port);
    println!("WebSockets server listening on {}", addr);
    ws::listen(addr, |out| WSServer {
        out: out,
        connections: connections.clone(),
        authorizer: DumbTicketStamper::new(connections.clone()),
    }).unwrap();
}
<commit_msg>Bugfix: handle error case where cookie exists but connection is gone<commit_after>//! sock
//!
//! Sock is a websocket server.
//! Sister server to `web`, which is the static file server.
use ws;
use time;
use std::fmt;
use uuid::Uuid;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::{HashMap};

mod msg;
mod cookie;
mod authorizer;

use self::cookie::{parse_cookies,put_cookie};
use game::{Action,Player,AnonymousPlayer};
use self::authorizer::{AuthorizesTicket,DumbTicketStamper};
use self::msg::{ActionMsg,parse};

#[derive(Debug, Clone)]
pub struct Connection {
    token  : ws::util::Token,
    ticket : Uuid,
    player : Player,
}

impl Connection {
    fn new(tk: ws::util::Token, tx: Uuid, p: Player) -> Connection {
        Connection {
            token : tk,
            ticket: tx,
            player: p,
        }
    }
}

impl fmt::Display for Connection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "conn[{:?}]{{{}}}", self.token, self.ticket)
    }
}

fn ws_log (evt: &str, token: ws::util::Token, msg: &str) {
    println!("ws:{}[{}][{:?}]: {}", evt, time::precise_time_ns(), token, msg)
}

fn ws_conn_log (evt: &str, con: &Connection, msg: &str) {
    println!("ws:{}[{}]:[{}]: {}", evt, time::precise_time_ns(), con, msg)
}

pub type ConnectionMap = Rc<RefCell<HashMap<ws::util::Token, Rc<RefCell<Connection>>>>>;

struct WSServer<T> {
    out: ws::Sender,
    connections: ConnectionMap,
    authorizer: T,
}

impl ws::Handler for WSServer<DumbTicketStamper> {
    fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
        let token = self.out.token();
        ws_log("req", token, "on_request received");

        let cookies: HashMap<String, String> = parse_cookies(req);
        let ticket: Option<Uuid> = cookies
            .get("bzwf_anon_wstx")
            .and_then(|uuid_string| Uuid::parse_str(uuid_string.as_str()).ok());

        let mut resp = ws::Response::from_request(req).unwrap();

        ticket.and_then(|ticket| {
            // If you have a ticket, you may have authenticated already.
            // Your connection was already established and has been upgraded.
            self.connections
                .borrow()
                .get(&token)
                .map(|conn| (ticket, conn.clone()))
        }).or_else(|| {
            // QUESTION(jordan): does lack of a ticket always mean anonymous player?
            // You do not have a ticket. Create a new Connection and authenticate anonymously.
            let ticket = Uuid::new_v4();
            let new_conn = Connection::new(token, ticket, AnonymousPlayer::new());
            ws_conn_log("req", &new_conn, "new connection");
            ws_conn_log("req", &new_conn, &format!("put cookie bzwf_anon_wstx={}", ticket));
            put_cookie(String::from("bzwf_anon_wstx"), ticket.to_string(), &mut resp);
            let conn = Rc::new(RefCell::new(new_conn));
            self.connections.borrow_mut().insert(token, conn.clone());
            self.connections.borrow().get(&token).map(|conn| (ticket, conn.clone()))
        })
        .map(|(ticket, conn)| {
            let conn = conn.borrow();
            let _ = self.authorizer.authorize_ticket(token, ticket)
                .map(|_| {
                    ws_conn_log("req", &conn, "ticket matches connection ticket");
                })
                .map_err(|err| {
                    ws_conn_log("req", &conn, &format!("err {}", err));
                });
        });

        ws_log("req", token, &format!("{} connected users", self.connections.borrow().len()));

        Ok(resp)
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        let token = self.out.token();

        let _ = parse(msg)
            .and_then(|msg_cell: ActionMsg| {
                let action: Action = msg_cell.val.clone();
                let ticket: Uuid = msg_cell.next.val;

                self.authorizer.authorize_ticket(token, ticket)
                    .map(|conn| (conn, action))
            })
            .map(|(conn, action)| {
                let Action::addItemToInventory(item) = action.clone();
                conn.borrow_mut().player.state().inventory.insert(item);
                ws_conn_log("rcv", &conn.borrow(), &format!("{:?}", action));
                println!("{:?}", conn.borrow_mut().player.state());
                let _ = self.out.send(format!("{:?}", conn.borrow_mut().player.state()));
            })
            .map_err(|err: String| {
                ws_log("rcv", token, &format!("err {}", err));
                let _ = self.out.send(err);
            });

        Ok(())
    }

    fn on_open(&mut self, hs: ws::Handshake) -> ws::Result<()> {
        // NOTE(jordan): There should be a Connection in the map, and a cookie matching the ticket.
        let token   = self.out.token();
        let cookies = parse_cookies(&hs.request);

        let _ = cookies.get("bzwf_anon_wstx")
            .ok_or(String::from("No bzwf_anon_wstx cookie!"))
            // ^^ TODO?(jordan): ws::Error?
            .and_then(|ticket| Uuid::parse_str(ticket).map_err(|_| String::from("invalid cookie")))
            .map(|ticket| (token, ticket))
            .and_then(|(token, ticket)| self.authorizer.authorize_ticket(token, ticket))
            .map(|conn| ws_conn_log("opn", &conn.borrow(), "cxn opened"))
            .map_err(|err| ws_log("opn", token, &format!("error: {}", err)));

        Ok(())
    }

    fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
        // TODO(jordan): remove a user's Connection struct
        let token = self.out.token();
        self.connections.borrow()
            .get(&token)
            .map(|conn| {
                ws_conn_log("cls", &conn.borrow(), &format!("cxn closed\n\tcode [{:?}] reason: {}", code, reason));
            });

        self.connections
            .borrow_mut()
            .remove(&token);

        ws_log("cls", token, &format!("{} users connected", self.connections.borrow().len()));
    }

    fn on_error(&mut self, err: ws::Error) {
        let token = self.out.token();
        // NOTE(jordan): this happens when we return a ws::Error. Handle it individually. match?
        ws_log("err", token, &format!("got error {}", err));
    }
}

pub fn server (domain: String, port: i32) -> () {
    let connections: ConnectionMap = Rc::new(RefCell::new(HashMap::new()));
    let addr: String = format!("{}:{}", domain, port);
    println!("WebSockets server listening on {}", addr);
    ws::listen(addr, |out| WSServer {
        out: out,
        connections: connections.clone(),
        authorizer: DumbTicketStamper::new(connections.clone()),
    }).unwrap();
}
<|endoftext|>


<commit_before>use ast::{Comment, CommentShape};
use itertools::Itertools;
use std::convert::identity;
use syntax::{
    ast::{self, edit::IndentLevel, CommentKind, Whitespace},
    AstToken, Direction, SyntaxElement, TextRange,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

/// Assist: line_to_block
///
/// Converts comments between block and single-line form
///
/// ```
///    // Multi-line
///    // comment
/// ```
/// ->
/// ```
///   /**
///   Multi-line
///   comment
///   */
/// ```
pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    if let Some(comment) = ctx.find_token_at_offset::<ast::Comment>() {
        // Only allow comments which are alone on their line
        if let Some(prev) = comment.syntax().prev_token() {
            if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() {
                return None;
            }
        }

        return match comment.kind().shape {
            ast::CommentShape::Block => block_to_line(acc, comment),
            ast::CommentShape::Line => line_to_block(acc, comment),
        };
    }

    return None;
}

fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
    let indentation = IndentLevel::from_token(comment.syntax());
    let line_prefix =
        comment_kind_prefix(CommentKind { shape: CommentShape::Line, ..comment.kind() });

    let text = comment.text();
    let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim();

    let lines = text.lines().peekable();

    let indent_spaces = indentation.to_string();
    let output = lines
        .map(|l| l.trim_start_matches(&indent_spaces))
        .map(|l| {
            // Don't introduce trailing whitespace
            if l.is_empty() {
                line_prefix.to_string()
            } else {
                format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces))
            }
        })
        .join(&format!("\n{}", indent_spaces));

    let target = comment.syntax().text_range();
    acc.add(
        AssistId("block_to_line", AssistKind::RefactorRewrite),
        "Replace block comment with line comments",
        target,
        |edit| edit.replace(target, output),
    )
}

fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
    // Find all the comments we'll be collapsing into a block
    let comments = relevant_line_comments(&comment);

    // Establish the target of our edit based on the comments we found
    let target = TextRange::new(
        comments[0].syntax().text_range().start(),
        comments.last().unwrap().syntax().text_range().end(),
    );

    // We pick a single indentation level for the whole block comment based on the
    // comment where the assist was invoked. This will be prepended to the
    // contents of each line comment when they're put into the block comment.
    let indentation = IndentLevel::from_token(&comment.syntax());

    let block_comment_body =
        comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");

    let block_prefix =
        comment_kind_prefix(CommentKind { shape: CommentShape::Block, ..comment.kind() });

    let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation.to_string());

    acc.add(
        AssistId("line_to_block", AssistKind::RefactorRewrite),
        "Replace line comments with a single block comment",
        target,
        |edit| edit.replace(target, output),
    )
}

/// The line -> block assist can  be invoked from anywhere within a sequence of line comments.
/// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will
/// be joined.
fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> {
    // The prefix identifies the kind of comment we're dealing with
    let prefix = comment.prefix();
    let same_prefix = |c: &ast::Comment| c.prefix() == prefix;

    // These tokens are allowed to exist between comments
    let skippable = |not: &SyntaxElement| {
        not.clone()
            .into_token()
            .and_then(Whitespace::cast)
            .map(|w| !w.spans_multiple_lines())
            .unwrap_or(false)
    };

    // Find all preceding comments (in reverse order) that have the same prefix
    let prev_comments = comment
        .syntax()
        .siblings_with_tokens(Direction::Prev)
        .filter(|s| !skippable(s))
        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
        .take_while(|opt_com| opt_com.is_some())
        .filter_map(identity)
        .skip(1); // skip the first element so we don't duplicate it in next_comments

    let next_comments = comment
        .syntax()
        .siblings_with_tokens(Direction::Next)
        .filter(|s| !skippable(s))
        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
        .take_while(|opt_com| opt_com.is_some())
        .filter_map(identity);

    let mut comments: Vec<_> = prev_comments.collect();
    comments.reverse();
    comments.extend(next_comments);
    comments
}

// Line comments usually begin with a single space character following the prefix as seen here:
//^
// But comments can also include indented text:
//    > Hello there
//
// We handle this by stripping *AT MOST* one space character from the start of the line
// This has its own problems because it can cause alignment issues:
//
//              /*
// a      ----> a
//b       ----> b
//              */
//
// But since such comments aren't idiomatic we're okay with this.
fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
    let contents = trim_one(comm.text().strip_prefix(comm.prefix()).unwrap()).to_owned();

    // Don't add the indentation if the line is empty
    if contents.is_empty() {
        contents
    } else {
        indentation.to_string() + &contents
    }
}

fn trim_one(text: &str) -> &str {
    if text.starts_with(' ') {
        &text[1..]
    } else {
        text
    }
}

fn comment_kind_prefix(ck: ast::CommentKind) -> &'static str {
    use ast::CommentPlacement::{Inner, Outer};
    use ast::CommentShape::{Block, Line};
    match (ck.shape, ck.doc) {
        (Line, Some(Inner)) => "//!",
        (Line, Some(Outer)) => "///",
        (Line, None) => "//",
        (Block, Some(Inner)) => "/*!",
        (Block, Some(Outer)) => "/**",
        (Block, None) => "/*",
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn single_line_to_block() {
        check_assist(
            convert_comment_block,
            r#"
// line$0 comment
fn main() {
    foo();
}
"#,
            r#"
/*
line comment
*/
fn main() {
    foo();
}
"#,
        );
    }

    #[test]
    fn single_line_to_block_indented() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // line$0 comment
    foo();
}
"#,
            r#"
fn main() {
    /*
    line comment
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn multiline_to_block() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // above
    // line$0 comment
    //
    // below
    foo();
}
"#,
            r#"
fn main() {
    /*
    above
    line comment

    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn end_of_line_to_block() {
        check_assist_not_applicable(
            convert_comment_block,
            r#"
fn main() {
    foo(); // end-of-line$0 comment
}
"#,
        );
    }

    #[test]
    fn single_line_different_kinds() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    /// different prefix
    // line$0 comment
    // below
    foo();
}
"#,
            r#"
fn main() {
    /// different prefix
    /*
    line comment
    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn single_line_separate_chunks() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // different chunk

    // line$0 comment
    // below
    foo();
}
"#,
            r#"
fn main() {
    // different chunk

    /*
    line comment
    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn doc_block_comment_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/**
 hi$0 there
*/
"#,
            r#"
/// hi there
"#,
        );
    }

    #[test]
    fn block_comment_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/*
 hi$0 there
*/
"#,
            r#"
// hi there
"#,
        );
    }

    #[test]
    fn inner_doc_block_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/*!
 hi$0 there
*/
"#,
            r#"
//! hi there
"#,
        );
    }

    #[test]
    fn block_to_lines_indent() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    /*!
    hi$0 there

    ```
      code_sample
    ```
    */
}
"#,
            r#"
fn main() {
    //! hi there
    //!
    //! ```
    //!   code_sample
    //! ```
}
"#,
        );
    }

    #[test]
    fn end_of_line_block_to_line() {
        check_assist_not_applicable(
            convert_comment_block,
            r#"
fn main() {
    foo(); /* end-of-line$0 comment */
}
"#,
        );
    }
}
<commit_msg>Apply edits<commit_after>use itertools::Itertools;
use std::convert::identity;
use syntax::{
    ast::{
        self,
        edit::IndentLevel,
        Comment, CommentKind,
        CommentPlacement::{Inner, Outer},
        CommentShape::{self, Block, Line},
        Whitespace,
    },
    AstToken, Direction, SyntaxElement, TextRange,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

/// Assist: line_to_block
///
/// Converts comments between block and single-line form
///
/// ```
///    // Multi-line
///    // comment
/// ```
/// ->
/// ```
///   /**
///   Multi-line
///   comment
///   */
/// ```
pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    if let Some(comment) = ctx.find_token_at_offset::<ast::Comment>() {
        // Only allow comments which are alone on their line
        if let Some(prev) = comment.syntax().prev_token() {
            if Whitespace::cast(prev).filter(|w| w.text().contains('\n')).is_none() {
                return None;
            }
        }

        return match comment.kind().shape {
            ast::CommentShape::Block => block_to_line(acc, comment),
            ast::CommentShape::Line => line_to_block(acc, comment),
        };
    }

    return None;
}

fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
    let target = comment.syntax().text_range();

    acc.add(
        AssistId("block_to_line", AssistKind::RefactorRewrite),
        "Replace block comment with line comments",
        target,
        |edit| {
            let indentation = IndentLevel::from_token(comment.syntax());
            let line_prefix =
                comment_kind_prefix(CommentKind { shape: CommentShape::Line, ..comment.kind() });

            let text = comment.text();
            let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim();

            let lines = text.lines().peekable();

            let indent_spaces = indentation.to_string();
            let output = lines
                .map(|l| l.trim_start_matches(&indent_spaces))
                .map(|l| {
                    // Don't introduce trailing whitespace
                    if l.is_empty() {
                        line_prefix.to_string()
                    } else {
                        format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces))
                    }
                })
                .join(&format!("\n{}", indent_spaces));

            edit.replace(target, output)
        },
    )
}

fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
    // Find all the comments we'll be collapsing into a block
    let comments = relevant_line_comments(&comment);

    // Establish the target of our edit based on the comments we found
    let target = TextRange::new(
        comments[0].syntax().text_range().start(),
        comments.last().unwrap().syntax().text_range().end(),
    );

    acc.add(
        AssistId("line_to_block", AssistKind::RefactorRewrite),
        "Replace line comments with a single block comment",
        target,
        |edit| {
            // We pick a single indentation level for the whole block comment based on the
            // comment where the assist was invoked. This will be prepended to the
            // contents of each line comment when they're put into the block comment.
            let indentation = IndentLevel::from_token(&comment.syntax());

            let block_comment_body =
                comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");

            let block_prefix =
                comment_kind_prefix(CommentKind { shape: CommentShape::Block, ..comment.kind() });

            let output =
                format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation.to_string());

            edit.replace(target, output)
        },
    )
}

/// The line -> block assist can  be invoked from anywhere within a sequence of line comments.
/// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will
/// be joined.
fn relevant_line_comments(comment: &ast::Comment) -> Vec<Comment> {
    // The prefix identifies the kind of comment we're dealing with
    let prefix = comment.prefix();
    let same_prefix = |c: &ast::Comment| c.prefix() == prefix;

    // These tokens are allowed to exist between comments
    let skippable = |not: &SyntaxElement| {
        not.clone()
            .into_token()
            .and_then(Whitespace::cast)
            .map(|w| !w.spans_multiple_lines())
            .unwrap_or(false)
    };

    // Find all preceding comments (in reverse order) that have the same prefix
    let prev_comments = comment
        .syntax()
        .siblings_with_tokens(Direction::Prev)
        .filter(|s| !skippable(s))
        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
        .take_while(|opt_com| opt_com.is_some())
        .filter_map(identity)
        .skip(1); // skip the first element so we don't duplicate it in next_comments

    let next_comments = comment
        .syntax()
        .siblings_with_tokens(Direction::Next)
        .filter(|s| !skippable(s))
        .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix))
        .take_while(|opt_com| opt_com.is_some())
        .filter_map(identity);

    let mut comments: Vec<_> = prev_comments.collect();
    comments.reverse();
    comments.extend(next_comments);
    comments
}

// Line comments usually begin with a single space character following the prefix as seen here:
//^
// But comments can also include indented text:
//    > Hello there
//
// We handle this by stripping *AT MOST* one space character from the start of the line
// This has its own problems because it can cause alignment issues:
//
//              /*
// a      ----> a
//b       ----> b
//              */
//
// But since such comments aren't idiomatic we're okay with this.
fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
    let contents_without_prefix = comm.text().strip_prefix(comm.prefix()).unwrap();
    let contents = contents_without_prefix.strip_prefix(' ').unwrap_or(contents_without_prefix);

    // Don't add the indentation if the line is empty
    if contents.is_empty() {
        contents.to_owned()
    } else {
        indentation.to_string() + &contents
    }
}

fn comment_kind_prefix(ck: ast::CommentKind) -> &'static str {
    match (ck.shape, ck.doc) {
        (Line, Some(Inner)) => "//!",
        (Line, Some(Outer)) => "///",
        (Line, None) => "//",
        (Block, Some(Inner)) => "/*!",
        (Block, Some(Outer)) => "/**",
        (Block, None) => "/*",
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn single_line_to_block() {
        check_assist(
            convert_comment_block,
            r#"
// line$0 comment
fn main() {
    foo();
}
"#,
            r#"
/*
line comment
*/
fn main() {
    foo();
}
"#,
        );
    }

    #[test]
    fn single_line_to_block_indented() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // line$0 comment
    foo();
}
"#,
            r#"
fn main() {
    /*
    line comment
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn multiline_to_block() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // above
    // line$0 comment
    //
    // below
    foo();
}
"#,
            r#"
fn main() {
    /*
    above
    line comment

    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn end_of_line_to_block() {
        check_assist_not_applicable(
            convert_comment_block,
            r#"
fn main() {
    foo(); // end-of-line$0 comment
}
"#,
        );
    }

    #[test]
    fn single_line_different_kinds() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    /// different prefix
    // line$0 comment
    // below
    foo();
}
"#,
            r#"
fn main() {
    /// different prefix
    /*
    line comment
    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn single_line_separate_chunks() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    // different chunk

    // line$0 comment
    // below
    foo();
}
"#,
            r#"
fn main() {
    // different chunk

    /*
    line comment
    below
    */
    foo();
}
"#,
        );
    }

    #[test]
    fn doc_block_comment_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/**
 hi$0 there
*/
"#,
            r#"
/// hi there
"#,
        );
    }

    #[test]
    fn block_comment_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/*
 hi$0 there
*/
"#,
            r#"
// hi there
"#,
        );
    }

    #[test]
    fn inner_doc_block_to_lines() {
        check_assist(
            convert_comment_block,
            r#"
/*!
 hi$0 there
*/
"#,
            r#"
//! hi there
"#,
        );
    }

    #[test]
    fn block_to_lines_indent() {
        check_assist(
            convert_comment_block,
            r#"
fn main() {
    /*!
    hi$0 there

    ```
      code_sample
    ```
    */
}
"#,
            r#"
fn main() {
    //! hi there
    //!
    //! ```
    //!   code_sample
    //! ```
}
"#,
        );
    }

    #[test]
    fn end_of_line_block_to_line() {
        check_assist_not_applicable(
            convert_comment_block,
            r#"
fn main() {
    foo(); /* end-of-line$0 comment */
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use ::io::Write;
use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
            }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_ISR(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_IRR(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct PICs (PIC, PIC);

impl PICs {
    const fn new() -> Self {
        PICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    fn initialize(&mut self) {
        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = unsafe { (self.0.data_port.in8(), self.1.data_port.in8()) };

        // Send both PICs the 'initialize' command.
        self.0.initialize();
        self.1.initialize();

        // Each PIC then expects us to send it the following:
        self.0.data_port
            .write(&[ self.0.offset     // 1. the PIC's new vector offset
                    , 0x04              // 2. number to configure PIC cascading
                    , Command::Mode8086 as u8 // 3. command for 8086 mode
                    , saved_mask1       // 4. finally, the mask we saved earlier
                    ]);

        self.1.data_port
            .write(&[ self.1.offset
                    , 0x02              // PIC2 gets 0x02 to set it as follower
                    , Command::Mode8086 as u8
                    , saved_mask2
                    ]);
    }
}

impl IRQHandler for PICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<PICs> = Mutex::new(PICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub fn initialize() {
    PICS.lock()
        .initialize()
}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<commit_msg>Expect PIC writes<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use ::io::Write;
use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
            }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_ISR(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_IRR(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct PICs (PIC, PIC);

impl PICs {
    const fn new() -> Self {
        PICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    fn initialize(&mut self) {
        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = unsafe { (self.0.data_port.in8(), self.1.data_port.in8()) };

        // Send both PICs the 'initialize' command.
        self.0.initialize();
        self.1.initialize();

        // Each PIC then expects us to send it the following:
        self.0.data_port
            .write(&[ self.0.offset     // 1. the PIC's new vector offset
                    , 0x04              // 2. number to configure PIC cascading
                    , Command::Mode8086 as u8 // 3. command for 8086 mode
                    , saved_mask1       // 4. finally, the mask we saved earlier
                    ]).expect("Could not write to PIC 0 data port!");

        self.1.data_port
            .write(&[ self.1.offset
                    , 0x02              // PIC2 gets 0x02 to set it as follower
                    , Command::Mode8086 as u8
                    , saved_mask2
                    ]).expect("Could not write to PIC 1 data port!");
    }
}

impl IRQHandler for PICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<PICs> = Mutex::new(PICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub fn initialize() {
    PICS.lock()
        .initialize()
}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<|endoftext|>


<commit_before>use bitstream::byteread::*;

trait BitReadEndian {
    fn peek_val(&mut self, n:usize) -> u64;
    fn merge_val(msp:u64, lsp:u64, msb:usize, lsb:usize) -> u64;
    fn skip_rem(&mut self, n:usize) -> ();
    fn fill32(&self) -> u64;
    fn fill64(&self) -> u64;
}

trait BitReadInternal {
    fn get_val(&mut self, n:usize) -> u64;
    fn refill32(&mut self) -> ();
    fn refill64(&mut self) -> ();
}

pub trait BitRead {
    fn consumed(&self) -> usize;
    fn available(&self) -> usize;
    fn can_refill(&self) -> bool;

    fn get_bit(&mut self) -> bool;
    fn get_bits_64(&mut self, n: usize) -> u64;
    fn get_bits_32(&mut self, n: usize) -> u32;

    fn peek_bits_32(&mut self, n: usize) -> u32;
    fn peek_bits_64(&mut self, n: usize) -> u64;

    fn skip_bits(&mut self, size : usize) -> ();
    fn align_bits(&mut self) -> ();
}

#[derive(Debug, Clone, Copy)]
pub struct BitReadLE <'a> {
    buffer : &'a[u8], /// read buffer, 8-bytes padded
    index : usize,
    cache : u64,
    left : usize,
}

impl <'a> BitReadEndian for BitReadLE<'a> {
    #[inline]
    fn peek_val(&mut self, n:usize) -> u64 {
        self.cache & ((1u64 << n) - 1)
    }
    #[inline]
    fn skip_rem(&mut self, n:usize) -> () {
        self.cache = self.cache >> n;
        self.left -= n;
    }
    #[inline]
    fn merge_val(msp:u64, lsp:u64, msb:usize, lsb:usize) -> u64 {
        msp << msb | lsp
    }
    #[inline(always)]
    fn fill32(&self) -> u64 {
        get_u32l(&self.buffer[self.index..]) as u64
    }
    #[inline(always)]
    fn fill64(&self) -> u64 {
        get_u64l(&self.buffer[self.index..])
    }
}

impl <'a> BitReadInternal for BitReadLE<'a> {
    #[inline]
    fn get_val(&mut self, n:usize) -> u64 {
        let ret = self.peek_val(n);

        self.skip_rem(n);

        return ret;
    }
    #[inline]
    fn refill32(&mut self) -> () {
        if !self.can_refill() {
            return;
        }
        let val = self.fill32();

        self.cache  = Self::merge_val(val, self.cache,
                                      self.left, 32 - self.left);
        self.index += 4;
        self.left  += 32;
    }
    #[inline]
    fn refill64(&mut self) -> () {
        if !self.can_refill() {
            return;
        }

        self.cache  = self.fill64();
        self.index += 8;
        self.left   = 64;
    }
}

impl <'a> BitRead for BitReadLE<'a> {
    #[inline]
    fn consumed(&self) -> usize {
        self.index * 8 - self.left
    }

    #[inline]
    fn available(&self) -> usize {
        (self.buffer.len() - self.index) * 8 + self.left
    }

    #[inline]
    fn can_refill(&self) -> bool {
        self.index < self.buffer.len() - 8
    }

    #[inline]
    fn get_bit(&mut self) -> bool {
        if self.left <= 0 {
            self.refill64();
        }

        self.get_val(1) != 0
    }

    #[inline]
    fn get_bits_64(&mut self, mut n:usize) -> u64 {
        let mut left = 0;
        let mut ret = 0;

        if n == 0 {
            return 0;
        }

        if self.left < n {
            n   -= left;
            left = self.left;
            ret  = self.get_val(left);
            self.refill64();
        }

        Self::merge_val(self.get_val(n), ret, left, n)
    }

    #[inline]
    fn peek_bits_64(&mut self, n:usize) -> u64 {
        let mut tmp = self.clone();

        tmp.get_bits_64(n)
    }

    #[inline]
    fn get_bits_32(&mut self, n:usize) -> u32 {
        if n == 0 {
            return 0;
        }

        if self.left <= n {
            self.refill32();
        }

        return self.get_val(n) as u32;
    }

    #[inline]
    fn peek_bits_32(&mut self, n:usize) -> u32 {
        if n == 0 {
            return 0;
        }

        if self.left <= n {
            self.refill32();
        }

        return self.peek_val(n) as u32;
    }

    #[inline]
    fn skip_bits(&mut self, mut n:usize) -> () {
        if self.left < n {
            n -= self.left;
            self.skip_rem(n);
            if n > 64 {
                let skip = n / 8;

                n -= skip * 8;
                self.index += skip;
            }
            self.refill64();
        }

        self.skip_rem(n);
    }

    #[inline]
    fn align_bits(&mut self) -> () {
        let left = self.left;

        self.skip_bits(left);
    }
}

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

    const CHECKBOARD0101: [u8; 128] = [0b01010101; 128];
    const CHECKBOARD0011: [u8; 128] = [0b00110011; 128];

#[test]
    fn get_bit() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.get_bit());
        assert!(!reader.get_bit());
    }

#[test]
    fn get_bits_64() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.get_bits_64(1) == 1);
        assert!(reader.get_bits_64(2) == 2);
        assert!(reader.get_bits_64(4) == 10);
        assert!(reader.get_bits_64(1) == 0);
        assert!(reader.get_bits_64(8) == 85);
    }

#[test]
    fn peek_bits_64() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.peek_bits_64(1) == 1);
        assert!(reader.peek_bits_64(1) == 1);
        assert!(reader.peek_bits_64(2) == 1);
        assert!(reader.peek_bits_64(2) == 1);
    }
#[test]
    fn get_bits_32() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.get_bits_64(1) == 1);
        assert!(reader.get_bits_64(2) == 2);
        assert!(reader.get_bits_64(4) == 10);
        assert!(reader.get_bits_64(1) == 0);
        assert!(reader.get_bits_64(8) == 85);
    }

#[test]
    fn peek_bits_32() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.peek_bits_32(1) == 1);
        assert!(reader.peek_bits_32(1) == 1);
        assert!(reader.peek_bits_32(2) == 1);
        assert!(reader.peek_bits_32(2) == 1);
    }
#[test]
    fn skip_bits() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0101,
            index: 0,
            cache: 0,
            left: 0
        };

        reader.skip_bits(0);
        assert!(reader.peek_bits_32(1) == 1);
        reader.skip_bits(2);
        assert!(reader.peek_bits_32(1) == 1);
        reader.skip_bits(2);
        assert!(reader.peek_bits_32(1) == 1);
    }
#[test]
    fn align_bits() {
        let mut reader = BitReadLE {
            buffer: &CHECKBOARD0011,
            index: 0,
            cache: 0,
            left: 0
        };

        assert!(reader.get_bits_64(3) == 3);
        reader.align_bits();
        assert!(reader.get_bits_64(4) == 3);
        reader.skip_bits(1);
        reader.align_bits();
        assert!(reader.get_bits_64(4) == 3);
    }
}
<commit_msg>bitstream: Implement BE variant<commit_after>use bitstream::byteread::*;

trait BitReadEndian {
    fn peek_val(&mut self, n:usize) -> u64;
    fn merge_val(msp:u64, lsp:u64, msb:usize, lsb:usize) -> u64;
    fn skip_rem(&mut self, n:usize) -> ();
    fn fill32(&self) -> u64;
    fn fill64(&self) -> u64;
}

trait BitReadInternal : BitReadEndian {
    fn get_val(&mut self, n:usize) -> u64;
    fn refill32(&mut self) -> ();
    fn refill64(&mut self) -> ();
}

pub trait BitRead<'a> {
    fn new(&'a[u8]) -> Self;
    fn consumed(&self) -> usize;
    fn available(&self) -> usize;
    fn can_refill(&self) -> bool;

    fn get_bit(&mut self) -> bool;
    fn get_bits_64(&mut self, n: usize) -> u64;
    fn get_bits_32(&mut self, n: usize) -> u32;

    fn peek_bits_32(&mut self, n: usize) -> u32;
    fn peek_bits_64(&mut self, n: usize) -> u64;

    fn skip_bits(&mut self, size : usize) -> ();
    fn align_bits(&mut self) -> ();
}

macro_rules! endian_reader {
    {$name: ident} => {
        #[derive(Debug, Clone, Copy)]
        pub struct $name<'a> {
            buffer : &'a[u8], /// read buffer, 8-bytes padded
            index : usize,
            cache : u64,
            left : usize,
        }
        impl <'a> BitReadInternal for $name<'a> {
            // #[inline]
            fn get_val(&mut self, n:usize) -> u64 {
                let ret = self.peek_val(n);

                self.skip_rem(n);

                return ret;
            }
            #[inline]
            fn refill32(&mut self) -> () {
                if !self.can_refill() {
                    return;
                }
                let val = self.fill32();

                self.cache  = Self::merge_val(val, self.cache,
                                              self.left, 32 - self.left);
                self.index += 4;
                self.left  += 32;
            }
            #[inline]
            fn refill64(&mut self) -> () {
                if !self.can_refill() {
                    return;
                }

                self.cache  = self.fill64();
                self.index += 8;
                self.left   = 64;
            }
        }

        impl <'a> BitRead<'a> for $name<'a> {
            fn new(buffer: &'a[u8]) -> $name<'a> {
                let mut reader = $name {
                    buffer: buffer,
                    index: 0,
                    cache: 0,
                    left: 0
                };

                reader.refill64();
                return reader;
            }
            #[inline]
            fn consumed(&self) -> usize {
                self.index * 8 - self.left
            }

            #[inline]
            fn available(&self) -> usize {
                (self.buffer.len() - self.index) * 8 + self.left
            }

            #[inline]
            fn can_refill(&self) -> bool {
                self.index < self.buffer.len() - 8
            }

            #[inline]
            fn get_bit(&mut self) -> bool {
                if self.left <= 0 {
                    self.refill64();
                }

                self.get_val(1) != 0
            }

            #[inline]
            fn get_bits_64(&mut self, mut n:usize) -> u64 {
                let mut left = 0;
                let mut ret = 0;

                if n == 0 {
                    return 0;
                }

                if self.left < n {
                    n   -= left;
                    left = self.left;
                    ret  = self.get_val(left);
                    self.refill64();
                }

                Self::merge_val(self.get_val(n), ret, left, n)
            }

            #[inline]
            fn peek_bits_64(&mut self, n:usize) -> u64 {
                let mut tmp = self.clone();

                tmp.get_bits_64(n)
            }

            #[inline]
            fn get_bits_32(&mut self, n:usize) -> u32 {
                if n == 0 {
                    return 0;
                }

                if self.left <= n {
                    self.refill32();
                }

                return self.get_val(n) as u32;
            }

            #[inline]
            fn peek_bits_32(&mut self, n:usize) -> u32 {
                if n == 0 {
                    return 0;
                }

                if self.left <= n {
                    self.refill32();
                }

                return self.peek_val(n) as u32;
            }

            #[inline]
            fn skip_bits(&mut self, mut n:usize) -> () {
                if n == 0 {
                    return;
                }
                if self.left <= n {
                    n -= self.left;
                    self.skip_rem(n);
                    if n > 64 {
                        let skip = n / 8;

                        n -= skip * 8;
                        self.index += skip;
                    }
                    self.refill64();
                }

                self.skip_rem(n);
            }

            #[inline]
            fn align_bits(&mut self) -> () {
                let left = self.left & 63;

                self.skip_bits(left);
            }
        }
    }
}

endian_reader!{ BitReadLE }

impl <'a> BitReadEndian for BitReadLE<'a> {
    #[inline]
    fn peek_val(&mut self, n:usize) -> u64 {
        let v = self.cache & ((1u64 << n) - 1);

        return v;
    }
    #[inline]
    fn skip_rem(&mut self, n:usize) -> () {
        self.cache = self.cache >> n;
        self.left -= n;
    }
    #[inline]
    fn merge_val(msp:u64, lsp:u64, msb:usize, _:usize) -> u64 {
        msp << msb | lsp
    }
    #[inline(always)]
    fn fill32(&self) -> u64 {
        get_u32l(&self.buffer[self.index..]) as u64
    }
    #[inline(always)]
    fn fill64(&self) -> u64 {
        get_u64l(&self.buffer[self.index..])
    }
}

endian_reader!{ BitReadBE }

impl <'a> BitReadEndian for BitReadBE<'a> {
    #[inline]
    fn peek_val(&mut self, n:usize) -> u64 {
        self.cache >> (64 - n)
    }
    #[inline]
    fn skip_rem(&mut self, n:usize) -> () {
        self.cache = self.cache << n;
        self.left -= n;
    }
    #[inline]
    fn merge_val(msp:u64, lsp:u64, _:usize, lsb:usize) -> u64 {
        msp | lsp << lsb
    }
    #[inline(always)]
    fn fill32(&self) -> u64 {
        get_u32b(&self.buffer[self.index..]) as u64
    }
    #[inline(always)]
    fn fill64(&self) -> u64 {
        get_u64b(&self.buffer[self.index..])
    }
}

#[cfg(test)]
mod test {
    pub const CHECKBOARD0101: [u8; 128] = [0b01010101; 128];
    pub const CHECKBOARD0011: [u8; 128] = [0b00110011; 128];

    #[test]
    fn blah() {
        struct Foo {
            cache : u64
        }
        fn skip_rem(f : &mut Foo, n:usize) -> () {
            f.cache = f.cache << n;
        }

        let mut f = Foo { cache : 0 };

        for n in 0..63 {
            skip_rem(&mut f, n);
        }
    }
/*
    mod le {
        use super::super::*;
        use super::*;

        #[test]
        fn get_bit() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadLE::new(b);

            assert!(reader.get_bit());
            assert!(!reader.get_bit());
        }

        #[test]
        fn get_bits_64() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadLE::new(b);

            assert!(reader.get_bits_64(1) == 1);
            assert!(reader.get_bits_64(2) == 2);
            assert!(reader.get_bits_64(4) == 10);
            assert!(reader.get_bits_64(1) == 0);
            assert!(reader.get_bits_64(8) == 85);
        }

        #[test]
        fn peek_bits_64() {
            let mut reader = BitReadLE {
                buffer: &CHECKBOARD0101,
                index: 0,
                cache: 0,
                left: 0
            };

            assert!(reader.peek_bits_64(1) == 1);
            assert!(reader.peek_bits_64(1) == 1);
            assert!(reader.peek_bits_64(2) == 1);
            assert!(reader.peek_bits_64(2) == 1);
        }

        #[test]
        fn get_bits_32() {
            let mut reader = BitReadLE {
                buffer: &CHECKBOARD0101,
                index: 0,
                cache: 0,
                left: 0
            };

            assert!(reader.get_bits_32(1) == 1);
            assert!(reader.get_bits_32(2) == 2);
            assert!(reader.get_bits_32(4) == 10);
            assert!(reader.get_bits_32(1) == 0);
            assert!(reader.get_bits_32(8) == 85);
        }

        #[test]
        fn peek_bits_32() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadLE::new(b);

            assert!(reader.peek_bits_32(1) == 1);
            assert!(reader.peek_bits_32(1) == 1);
            assert!(reader.peek_bits_32(2) == 1);
            assert!(reader.peek_bits_32(2) == 1);
        }

        #[test]
        fn skip_bits() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadLE::new(b);

            reader.skip_bits(0);
            assert!(reader.peek_bits_32(1) == 1);
            reader.skip_bits(2);
            assert!(reader.peek_bits_32(1) == 1);
            reader.skip_bits(2);
            assert!(reader.peek_bits_32(1) == 1);
        }

        #[test]
        fn align_bits() {
            let b = &CHECKBOARD0011;
            let mut reader = BitReadLE::new(b);

            reader.align_bits();
            assert!(reader.get_bits_64(3) == 3);
            reader.align_bits();
            assert!(reader.get_bits_64(4) == 3);
            reader.skip_bits(1);
            reader.align_bits();
            assert!(reader.get_bits_64(4) == 3);
        }
    }
*/
    mod be {
        use super::super::*;
        use super::*;

        #[test]
        fn get_bit() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            assert!(!reader.get_bit());
            assert!(reader.get_bit());
        }

        #[test]
        fn get_bits_64() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            assert!(reader.get_bits_64(1) == 0);
            assert!(reader.get_bits_64(2) == 2);
            assert!(reader.get_bits_64(4) == 10);
            assert!(reader.get_bits_64(1) == 1);
            assert!(reader.get_bits_64(8) == 85);
        }

        #[test]
        fn peek_bits_64() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            assert!(reader.peek_bits_64(1) == 0);
            assert!(reader.peek_bits_64(1) == 0);
            assert!(reader.peek_bits_64(2) == 1);
            assert!(reader.peek_bits_64(2) == 1);
        }

        #[test]
        fn get_bits_32() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            assert!(reader.get_bits_32(1) == 0);
            assert!(reader.get_bits_32(2) == 2);
            assert!(reader.get_bits_32(4) == 10);
            assert!(reader.get_bits_32(1) == 1);
            assert!(reader.get_bits_32(8) == 85);
        }

        #[test]
        fn peek_bits_32() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            assert!(reader.peek_bits_32(1) == 0);
            assert!(reader.peek_bits_32(1) == 0);
            assert!(reader.peek_bits_32(2) == 1);
            assert!(reader.peek_bits_32(2) == 1);
        }

        #[test]
        fn skip_bits() {
            let b = &CHECKBOARD0101;
            let mut reader = BitReadBE::new(b);

            reader.skip_bits(0);
            assert!(reader.peek_bits_32(1) == 0);
            reader.skip_bits(2);
            assert!(reader.peek_bits_32(1) == 0);
            reader.skip_bits(2);
            assert!(reader.peek_bits_32(1) == 0);
        }

        #[test]
        fn align_bits() {
            let b = &CHECKBOARD0011;
            let mut reader = BitReadBE::new(b);

            reader.align_bits();
            assert!(reader.get_bits_64(3) == 1);
            reader.align_bits();
            assert!(reader.get_bits_64(4) == 3);
            reader.skip_bits(1);
            reader.align_bits();
            assert!(reader.get_bits_64(4) == 3);
        }
    }

}
<|endoftext|>


<commit_before>//! `assists` crate provides a bunch of code assists, also known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod assist_config;
mod assist_context;
#[cfg(test)]
mod tests;
pub mod utils;
pub mod ast_transform;

use hir::Semantics;
use ide_db::base_db::FileRange;
use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
use syntax::TextRange;

pub(crate) use crate::assist_context::{AssistContext, Assists};

pub use assist_config::AssistConfig;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssistKind {
    None,
    QuickFix,
    Generate,
    Refactor,
    RefactorExtract,
    RefactorInline,
    RefactorRewrite,
}

impl AssistKind {
    pub fn contains(self, other: AssistKind) -> bool {
        if self == other {
            return true;
        }

        match self {
            AssistKind::None | AssistKind::Generate => return true,
            AssistKind::Refactor => match other {
                AssistKind::RefactorExtract
                | AssistKind::RefactorInline
                | AssistKind::RefactorRewrite => return true,
                _ => return false,
            },
            _ => return false,
        }
    }
}

/// Unique identifier of the assist, should not be shown to the user
/// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str, pub AssistKind);

#[derive(Clone, Debug)]
pub struct GroupLabel(pub String);

#[derive(Debug, Clone)]
pub struct Assist {
    pub id: AssistId,
    /// Short description of the assist, as shown in the UI.
    pub label: Label,
    pub group: Option<GroupLabel>,
    /// Target ranges are used to sort assists: the smaller the target range,
    /// the more specific assist is, and so it should be sorted first.
    pub target: TextRange,
}

#[derive(Debug, Clone)]
pub struct ResolvedAssist {
    pub assist: Assist,
    pub source_change: SourceChange,
}

impl Assist {
    /// Return all the assists applicable at the given position.
    ///
    /// Assists are returned in the "unresolved" state, that is only labels are
    /// returned, without actual edits.
    pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
        let sema = Semantics::new(db);
        let ctx = AssistContext::new(sema, config, range);
        let mut acc = Assists::new_unresolved(&ctx);
        handlers::all().iter().for_each(|handler| {
            handler(&mut acc, &ctx);
        });
        acc.finish_unresolved()
    }

    /// Return all the assists applicable at the given position.
    ///
    /// Assists are returned in the "resolved" state, that is with edit fully
    /// computed.
    pub fn resolved(
        db: &RootDatabase,
        config: &AssistConfig,
        range: FileRange,
    ) -> Vec<ResolvedAssist> {
        let sema = Semantics::new(db);
        let ctx = AssistContext::new(sema, config, range);
        let mut acc = Assists::new_resolved(&ctx);
        handlers::all().iter().for_each(|handler| {
            handler(&mut acc, &ctx);
        });
        acc.finish_resolved()
    }
}

mod handlers {
    use crate::{AssistContext, Assists};

    pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;

    mod add_custom_impl;
    mod add_explicit_type;
    mod add_missing_impl_members;
    mod add_turbo_fish;
    mod apply_demorgan;
    mod auto_import;
    mod change_return_type_to_result;
    mod change_visibility;
    mod convert_integer_literal;
    mod early_return;
    mod expand_glob_import;
    mod extract_struct_from_enum_variant;
    mod extract_variable;
    mod fill_match_arms;
    mod fix_visibility;
    mod flip_binexpr;
    mod flip_comma;
    mod flip_trait_bound;
    mod generate_derive;
    mod generate_from_impl_for_enum;
    mod generate_function;
    mod generate_impl;
    mod generate_new;
    mod inline_local_variable;
    mod introduce_named_lifetime;
    mod invert_if;
    mod merge_imports;
    mod merge_match_arms;
    mod move_bounds;
    mod move_guard;
    mod qualify_path;
    mod raw_string;
    mod remove_dbg;
    mod remove_mut;
    mod remove_unused_param;
    mod reorder_fields;
    mod replace_if_let_with_match;
    mod replace_impl_trait_with_generic;
    mod replace_let_with_if_let;
    mod replace_qualified_name_with_use;
    mod replace_string_with_char;
    mod replace_unwrap_with_match;
    mod split_import;
    mod unwrap_block;

    pub(crate) fn all() -> &'static [Handler] {
        &[
            // These are alphabetic for the foolish consistency
            add_custom_impl::add_custom_impl,
            add_explicit_type::add_explicit_type,
            add_turbo_fish::add_turbo_fish,
            apply_demorgan::apply_demorgan,
            auto_import::auto_import,
            change_return_type_to_result::change_return_type_to_result,
            change_visibility::change_visibility,
            convert_integer_literal::convert_integer_literal,
            early_return::convert_to_guarded_return,
            expand_glob_import::expand_glob_import,
            extract_struct_from_enum_variant::extract_struct_from_enum_variant,
            extract_variable::extract_variable,
            fill_match_arms::fill_match_arms,
            fix_visibility::fix_visibility,
            flip_binexpr::flip_binexpr,
            flip_comma::flip_comma,
            flip_trait_bound::flip_trait_bound,
            generate_derive::generate_derive,
            generate_from_impl_for_enum::generate_from_impl_for_enum,
            generate_function::generate_function,
            generate_impl::generate_impl,
            generate_new::generate_new,
            inline_local_variable::inline_local_variable,
            introduce_named_lifetime::introduce_named_lifetime,
            invert_if::invert_if,
            merge_imports::merge_imports,
            merge_match_arms::merge_match_arms,
            move_bounds::move_bounds_to_where_clause,
            move_guard::move_arm_cond_to_match_guard,
            move_guard::move_guard_to_arm_body,
            qualify_path::qualify_path,
            raw_string::add_hash,
            raw_string::make_raw_string,
            raw_string::make_usual_string,
            raw_string::remove_hash,
            remove_dbg::remove_dbg,
            remove_mut::remove_mut,
            remove_unused_param::remove_unused_param,
            reorder_fields::reorder_fields,
            replace_if_let_with_match::replace_if_let_with_match,
            replace_impl_trait_with_generic::replace_impl_trait_with_generic,
            replace_let_with_if_let::replace_let_with_if_let,
            replace_qualified_name_with_use::replace_qualified_name_with_use,
            replace_string_with_char::replace_string_with_char,
            replace_unwrap_with_match::replace_unwrap_with_match,
            split_import::split_import,
            unwrap_block::unwrap_block,
            // These are manually sorted for better priorities
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            // Are you sure you want to add new assist here, and not to the
            // sorted list above?
        ]
    }
}
<commit_msg>Better ordering of assists<commit_after>//! `assists` crate provides a bunch of code assists, also known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod assist_config;
mod assist_context;
#[cfg(test)]
mod tests;
pub mod utils;
pub mod ast_transform;

use hir::Semantics;
use ide_db::base_db::FileRange;
use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
use syntax::TextRange;

pub(crate) use crate::assist_context::{AssistContext, Assists};

pub use assist_config::AssistConfig;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssistKind {
    None,
    QuickFix,
    Generate,
    Refactor,
    RefactorExtract,
    RefactorInline,
    RefactorRewrite,
}

impl AssistKind {
    pub fn contains(self, other: AssistKind) -> bool {
        if self == other {
            return true;
        }

        match self {
            AssistKind::None | AssistKind::Generate => return true,
            AssistKind::Refactor => match other {
                AssistKind::RefactorExtract
                | AssistKind::RefactorInline
                | AssistKind::RefactorRewrite => return true,
                _ => return false,
            },
            _ => return false,
        }
    }
}

/// Unique identifier of the assist, should not be shown to the user
/// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str, pub AssistKind);

#[derive(Clone, Debug)]
pub struct GroupLabel(pub String);

#[derive(Debug, Clone)]
pub struct Assist {
    pub id: AssistId,
    /// Short description of the assist, as shown in the UI.
    pub label: Label,
    pub group: Option<GroupLabel>,
    /// Target ranges are used to sort assists: the smaller the target range,
    /// the more specific assist is, and so it should be sorted first.
    pub target: TextRange,
}

#[derive(Debug, Clone)]
pub struct ResolvedAssist {
    pub assist: Assist,
    pub source_change: SourceChange,
}

impl Assist {
    /// Return all the assists applicable at the given position.
    ///
    /// Assists are returned in the "unresolved" state, that is only labels are
    /// returned, without actual edits.
    pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
        let sema = Semantics::new(db);
        let ctx = AssistContext::new(sema, config, range);
        let mut acc = Assists::new_unresolved(&ctx);
        handlers::all().iter().for_each(|handler| {
            handler(&mut acc, &ctx);
        });
        acc.finish_unresolved()
    }

    /// Return all the assists applicable at the given position.
    ///
    /// Assists are returned in the "resolved" state, that is with edit fully
    /// computed.
    pub fn resolved(
        db: &RootDatabase,
        config: &AssistConfig,
        range: FileRange,
    ) -> Vec<ResolvedAssist> {
        let sema = Semantics::new(db);
        let ctx = AssistContext::new(sema, config, range);
        let mut acc = Assists::new_resolved(&ctx);
        handlers::all().iter().for_each(|handler| {
            handler(&mut acc, &ctx);
        });
        acc.finish_resolved()
    }
}

mod handlers {
    use crate::{AssistContext, Assists};

    pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;

    mod add_custom_impl;
    mod add_explicit_type;
    mod add_missing_impl_members;
    mod add_turbo_fish;
    mod apply_demorgan;
    mod auto_import;
    mod change_return_type_to_result;
    mod change_visibility;
    mod convert_integer_literal;
    mod early_return;
    mod expand_glob_import;
    mod extract_struct_from_enum_variant;
    mod extract_variable;
    mod fill_match_arms;
    mod fix_visibility;
    mod flip_binexpr;
    mod flip_comma;
    mod flip_trait_bound;
    mod generate_derive;
    mod generate_from_impl_for_enum;
    mod generate_function;
    mod generate_impl;
    mod generate_new;
    mod inline_local_variable;
    mod introduce_named_lifetime;
    mod invert_if;
    mod merge_imports;
    mod merge_match_arms;
    mod move_bounds;
    mod move_guard;
    mod qualify_path;
    mod raw_string;
    mod remove_dbg;
    mod remove_mut;
    mod remove_unused_param;
    mod reorder_fields;
    mod replace_if_let_with_match;
    mod replace_impl_trait_with_generic;
    mod replace_let_with_if_let;
    mod replace_qualified_name_with_use;
    mod replace_string_with_char;
    mod replace_unwrap_with_match;
    mod split_import;
    mod unwrap_block;

    pub(crate) fn all() -> &'static [Handler] {
        &[
            // These are alphabetic for the foolish consistency
            add_custom_impl::add_custom_impl,
            add_explicit_type::add_explicit_type,
            add_turbo_fish::add_turbo_fish,
            apply_demorgan::apply_demorgan,
            auto_import::auto_import,
            change_return_type_to_result::change_return_type_to_result,
            change_visibility::change_visibility,
            convert_integer_literal::convert_integer_literal,
            early_return::convert_to_guarded_return,
            expand_glob_import::expand_glob_import,
            extract_struct_from_enum_variant::extract_struct_from_enum_variant,
            extract_variable::extract_variable,
            fill_match_arms::fill_match_arms,
            fix_visibility::fix_visibility,
            flip_binexpr::flip_binexpr,
            flip_comma::flip_comma,
            flip_trait_bound::flip_trait_bound,
            generate_derive::generate_derive,
            generate_from_impl_for_enum::generate_from_impl_for_enum,
            generate_function::generate_function,
            generate_impl::generate_impl,
            generate_new::generate_new,
            inline_local_variable::inline_local_variable,
            introduce_named_lifetime::introduce_named_lifetime,
            invert_if::invert_if,
            merge_imports::merge_imports,
            merge_match_arms::merge_match_arms,
            move_bounds::move_bounds_to_where_clause,
            move_guard::move_arm_cond_to_match_guard,
            move_guard::move_guard_to_arm_body,
            qualify_path::qualify_path,
            raw_string::add_hash,
            raw_string::make_usual_string,
            raw_string::remove_hash,
            remove_dbg::remove_dbg,
            remove_mut::remove_mut,
            remove_unused_param::remove_unused_param,
            reorder_fields::reorder_fields,
            replace_if_let_with_match::replace_if_let_with_match,
            replace_impl_trait_with_generic::replace_impl_trait_with_generic,
            replace_let_with_if_let::replace_let_with_if_let,
            replace_qualified_name_with_use::replace_qualified_name_with_use,
            replace_unwrap_with_match::replace_unwrap_with_match,
            split_import::split_import,
            unwrap_block::unwrap_block,
            // These are manually sorted for better priorities
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            //
            replace_string_with_char::replace_string_with_char,
            raw_string::make_raw_string,
            // Are you sure you want to add new assist here, and not to the
            // sorted list above?
        ]
    }
}
<|endoftext|>