<commit_before>use ws;
use time;
use std::fmt;
use uuid::Uuid;
use std::sync::{Arc,Mutex};
use std::collections::{HashMap,HashSet};

mod authorizer;

use self::authorizer::AuthorizesTicket;
use self::authorizer::DumbTicketStamper;

#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub enum Item {
    Potatoes,
    Berries,
    TreeSap,
}

trait ParseFrom<T, Out = Self> {
    fn parse (from: T) -> Result<Out, String>;
}

impl ParseFrom<i32> for Item {
    fn parse (i: i32) -> Result<Self, String> {
        match i {
            0 => Ok(Item::Potatoes),
            1 => Ok(Item::Berries),
            2 => Ok(Item::TreeSap),
            _ => Err(format!("Item.parse<i32> failure: unrecognized Item {}", i)),
        }
    }
}

#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum Action {
    addItemToInventory(Item),
}

#[derive(Debug, Clone)]
pub struct PlayerState {
    inventory: HashSet<Item>,
}

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum Player {
    AnonymousPlayer {
        state : PlayerState,
    },
    RegisteredPlayer {
        id    : i32,
        name  : String,
        state : PlayerState,
    }
}

#[allow(non_snake_case)]
mod AnonymousPlayer {
    use super::*;
    pub fn new () -> Player {
        let mut new_player_inventory = HashSet::new();
        new_player_inventory.insert(Item::Potatoes);

        Player::AnonymousPlayer {
            state: PlayerState {
                inventory: new_player_inventory
            }
        }
    }
}

#[allow(non_snake_case,dead_code)]
mod RegisteredPlayer {
    use super::*;
    pub fn new (id: i32, name: String) -> Player {
        let mut new_player_inventory = HashSet::new();
        new_player_inventory.insert(Item::Potatoes);

        Player::RegisteredPlayer {
            id: id,
            name: name,
            state: PlayerState {
                inventory: new_player_inventory
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct Connection {
    uuid   : Uuid,
    player : Player,
}

impl Connection {
    fn new(u: Uuid, p: Player) -> Connection {
        Connection {
            uuid  : u,
            player: p
        }
    }
}

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

fn parse_cookies (req: &ws::Request) -> HashMap<String, String> {
    req.header("cookie")
        .and_then(|cookies_bytes| String::from_utf8(cookies_bytes.to_vec()).ok())
        .unwrap_or(String::from(""))
        .rsplit(";")
        .filter_map(|cookie_string| {
            let mut cookie_pair = cookie_string.split("=");
            match (cookie_pair.next(), cookie_pair.next()) {
                (Some(name), Some(value)) => {
                    Some((String::from(name.trim()), String::from(value.trim())))
                },
                _ => None
            }
        })
        .collect()
}

fn put_cookie (name: String, value: String, resp: &mut ws::Response) {
    let headers = resp.headers_mut();
    let cookie_bytes = format!("{}={}", name, value).as_bytes().to_vec();
    headers.push((String::from("Set-Cookie"), cookie_bytes));
}

pub type ConnectionMap = Arc<Mutex<HashMap<Uuid, Connection>>>;

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

fn parse_message_ticket (msg: ws::Message) -> Result<(Uuid, Vec<String>), String> {
    fn parse_error_msg (reason: &str) -> String {
        format!("parse_message_ticket failure: {}", reason)
    }

    msg.into_text()
        .or(Err(parse_error_msg("cannot get text from Message; is it a binary Message?")))
        ?.split(":")                         // String -> Split<'_, &str>
        .map(|p| p.to_string())              // Split<'_, &str> -> &[String]
        .collect::<Vec<String>>()            // &[String] -> Vec<String>
        .split_first()                       // Vec<String> -> Option<(String, Vec<String>)>
        .ok_or(parse_error_msg("no ticket")) // Option -> Result<(String, &[String]), String>
        .and_then(|(uuid_string, rest)| {    // Result<(String, &[String]), String>
            Uuid::parse_str(uuid_string)     //   |-> Result<(Uuid, Vec<String>), String>
                .or(Err(parse_error_msg("ticket is invalid uuidv4")))
                .map(|uuid| (uuid, Vec::from(rest)))
        })
}

fn parse_message_type(msg_contents: Vec<String>) -> Result<(String, Vec<String>), String> {
    msg_contents.split_first()
        .ok_or(String::from("missing message type and params"))
        .and_then(|(f, r)| {
            if f.len() == 0 {
                Err(String::from("msg_type is empty"))
            } else {
                Ok((f.to_owned(), Vec::from(r)))
            }
        })
}

fn parse_message_action((msg_type, msg_params): (String, Vec<String>)) -> Result<Action, String> {
    println!("trying to read contents of message {}({})", msg_type, msg_params.join(","));

    // TODO(jordan): replace with Action ParseFrom<String> impl
    match msg_type.as_str() {
        "addItemToInventory" => {
            if msg_params.len() != 1 {
                Err(String::from("parse_message failure: addItemToInventory: no item code"))
            } else if let Ok(item_num) = msg_params[0].parse::<i32>() {
                Item::parse(item_num)
                    .map(|item| Action::addItemToInventory(item))
            } else {
                Err(String::from("parse_message failure: addItemToInventory: invalid i32 item code"))
            }
        },
        _ => Err(String::from("parse_message failure: unrecognized message type"))
    }
}

impl ws::Handler for WSServer<DumbTicketStamper> {
    fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
        let mut resp = ws::Response::from_request(req).unwrap();

        let mut conn_map = self.connections.lock().unwrap();

        println!("ws:req[{}]", time::precise_time_ns());

        let cookies : HashMap<String, String> = parse_cookies(req);

        let mut cookie_existed = true;
        let ticket = cookies.get("bzwf_anon_wstx")
            .and_then(|uuid_string| Uuid::parse_str(uuid_string.as_str()).ok())
            .unwrap_or_else(|| {
                println!("ws:req[{}]: no bzwf_anon_wstx cookie found", time::precise_time_ns());
                cookie_existed = false;
                Uuid::new_v4()
            });

        let users_count = conn_map.len();

        if conn_map.contains_key(&ticket) {
            println!("ws:req[{}]: reconnect: uuid {}", time::precise_time_ns(), ticket);
            println!("{} connected users", users_count);
        } else {
            println!("ws:req[{}]: new connection with uuid {}", time::precise_time_ns(), ticket);

            let cookie_op_str = if cookie_existed {
                "replacing persistence cookie"
            } else {
                "creating persistence cookie"
            };

            println!("ws:req[{}]: {}: bzwf_anon_wstx={}", time::precise_time_ns(), cookie_op_str, ticket);
            println!("{} connected users", users_count + 1);
            put_cookie(String::from("bzwf_anon_wstx"), ticket.to_string(), &mut resp);
            conn_map.insert(ticket, Connection::new(ticket, AnonymousPlayer::new()));
        }

        Ok(resp)
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        println!("ws:rcv[{}]: user with uuid {} sent ws msg {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 msg);

        let _ = parse_message_ticket(msg)
            .and_then(|t| self.authorizer.authorize_ticket(t))
            .and_then(parse_message_type)
            .and_then(parse_message_action)
            .map(|action| {
                self.out.send(format!("gotcha, you want to {:?}", action))
            })
            .unwrap_or_else(|err| {
                println!("{}", err);
                self.out.send("got your message, but not sure what it meant")
            });

        Ok(())
    }

    fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
        // TODO
        println!("ws:opn[{}]: user with uuid {} opened ws cxn",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]");
        Ok(())
    }

    fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
        println!("ws:cls[{}]: user with uuid {} closed ws cxn\n\tCode [{:?}] reason: {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 code,
                 reason);
        // TODO
    }

    fn on_error(&mut self, err: ws::Error) {
        println!("ws:err[{}]: user with uuid {} got error {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 err);
        // TODO
    }
}

pub fn server (domain: String, port: i32) -> () {
    let connections : ConnectionMap = Arc::new(Mutex::new(HashMap::new()));
    let addr = 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>Refactor/Minor: simpler cookie existence check<commit_after>use ws;
use time;
use std::fmt;
use uuid::Uuid;
use std::sync::{Arc,Mutex};
use std::collections::{HashMap,HashSet};

mod authorizer;

use self::authorizer::AuthorizesTicket;
use self::authorizer::DumbTicketStamper;

#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub enum Item {
    Potatoes,
    Berries,
    TreeSap,
}

trait ParseFrom<T, Out = Self> {
    fn parse (from: T) -> Result<Out, String>;
}

impl ParseFrom<i32> for Item {
    fn parse (i: i32) -> Result<Self, String> {
        match i {
            0 => Ok(Item::Potatoes),
            1 => Ok(Item::Berries),
            2 => Ok(Item::TreeSap),
            _ => Err(format!("Item.parse<i32> failure: unrecognized Item {}", i)),
        }
    }
}

#[derive(Debug)]
#[allow(non_camel_case_types)]
pub enum Action {
    addItemToInventory(Item),
}

#[derive(Debug, Clone)]
pub struct PlayerState {
    inventory: HashSet<Item>,
}

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum Player {
    AnonymousPlayer {
        state : PlayerState,
    },
    RegisteredPlayer {
        id    : i32,
        name  : String,
        state : PlayerState,
    }
}

#[allow(non_snake_case)]
mod AnonymousPlayer {
    use super::*;
    pub fn new () -> Player {
        let mut new_player_inventory = HashSet::new();
        new_player_inventory.insert(Item::Potatoes);

        Player::AnonymousPlayer {
            state: PlayerState {
                inventory: new_player_inventory
            }
        }
    }
}

#[allow(non_snake_case,dead_code)]
mod RegisteredPlayer {
    use super::*;
    pub fn new (id: i32, name: String) -> Player {
        let mut new_player_inventory = HashSet::new();
        new_player_inventory.insert(Item::Potatoes);

        Player::RegisteredPlayer {
            id: id,
            name: name,
            state: PlayerState {
                inventory: new_player_inventory
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct Connection {
    uuid   : Uuid,
    player : Player,
}

impl Connection {
    fn new(u: Uuid, p: Player) -> Connection {
        Connection {
            uuid  : u,
            player: p
        }
    }
}

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

fn parse_cookies (req: &ws::Request) -> HashMap<String, String> {
    req.header("cookie")
        .and_then(|cookies_bytes| String::from_utf8(cookies_bytes.to_vec()).ok())
        .unwrap_or(String::from(""))
        .rsplit(";")
        .filter_map(|cookie_string| {
            let mut cookie_pair = cookie_string.split("=");
            match (cookie_pair.next(), cookie_pair.next()) {
                (Some(name), Some(value)) => {
                    Some((String::from(name.trim()), String::from(value.trim())))
                },
                _ => None
            }
        })
        .collect()
}

fn put_cookie (name: String, value: String, resp: &mut ws::Response) {
    let headers = resp.headers_mut();
    let cookie_bytes = format!("{}={}", name, value).as_bytes().to_vec();
    headers.push((String::from("Set-Cookie"), cookie_bytes));
}

pub type ConnectionMap = Arc<Mutex<HashMap<Uuid, Connection>>>;

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

fn parse_message_ticket (msg: ws::Message) -> Result<(Uuid, Vec<String>), String> {
    fn parse_error_msg (reason: &str) -> String {
        format!("parse_message_ticket failure: {}", reason)
    }

    msg.into_text()
        .or(Err(parse_error_msg("cannot get text from Message; is it a binary Message?")))
        ?.split(":")                         // String -> Split<'_, &str>
        .map(|p| p.to_string())              // Split<'_, &str> -> &[String]
        .collect::<Vec<String>>()            // &[String] -> Vec<String>
        .split_first()                       // Vec<String> -> Option<(String, Vec<String>)>
        .ok_or(parse_error_msg("no ticket")) // Option -> Result<(String, &[String]), String>
        .and_then(|(uuid_string, rest)| {    // Result<(String, &[String]), String>
            Uuid::parse_str(uuid_string)     //   |-> Result<(Uuid, Vec<String>), String>
                .or(Err(parse_error_msg("ticket is invalid uuidv4")))
                .map(|uuid| (uuid, Vec::from(rest)))
        })
}

fn parse_message_type(msg_contents: Vec<String>) -> Result<(String, Vec<String>), String> {
    msg_contents.split_first()
        .ok_or(String::from("missing message type and params"))
        .and_then(|(f, r)| {
            if f.len() == 0 {
                Err(String::from("msg_type is empty"))
            } else {
                Ok((f.to_owned(), Vec::from(r)))
            }
        })
}

fn parse_message_action((msg_type, msg_params): (String, Vec<String>)) -> Result<Action, String> {
    println!("trying to read contents of message {}({})", msg_type, msg_params.join(","));

    // TODO(jordan): replace with Action ParseFrom<String> impl
    match msg_type.as_str() {
        "addItemToInventory" => {
            if msg_params.len() != 1 {
                Err(String::from("parse_message failure: addItemToInventory: no item code"))
            } else if let Ok(item_num) = msg_params[0].parse::<i32>() {
                Item::parse(item_num)
                    .map(|item| Action::addItemToInventory(item))
            } else {
                Err(String::from("parse_message failure: addItemToInventory: invalid i32 item code"))
            }
        },
        _ => Err(String::from("parse_message failure: unrecognized message type"))
    }
}

impl ws::Handler for WSServer<DumbTicketStamper> {
    fn on_request(&mut self, req: &ws::Request) -> ws::Result<ws::Response> {
        let mut resp = ws::Response::from_request(req).unwrap();

        let mut conn_map = self.connections.lock().unwrap();

        println!("ws:req[{}]", time::precise_time_ns());

        let cookies : HashMap<String, String> = parse_cookies(req);

        let cookie_existed = cookies.contains_key("bzwf_anon_wstx");
        let ticket = cookies.get("bzwf_anon_wstx")
            .and_then(|uuid_string| Uuid::parse_str(uuid_string.as_str()).ok())
            .unwrap_or_else(|| {
                println!("ws:req[{}]: no bzwf_anon_wstx cookie found", time::precise_time_ns());
                Uuid::new_v4()
            });

        let users_count = conn_map.len();

        if conn_map.contains_key(&ticket) {
            println!("ws:req[{}]: reconnect: uuid {}", time::precise_time_ns(), ticket);
            println!("{} connected users", users_count);
        } else {
            println!("ws:req[{}]: new connection with uuid {}", time::precise_time_ns(), ticket);

            let cookie_op_str = if cookie_existed {
                "replacing persistence cookie"
            } else {
                "creating persistence cookie"
            };

            println!("ws:req[{}]: {}: bzwf_anon_wstx={}", time::precise_time_ns(), cookie_op_str, ticket);
            println!("{} connected users", users_count + 1);
            put_cookie(String::from("bzwf_anon_wstx"), ticket.to_string(), &mut resp);
            conn_map.insert(ticket, Connection::new(ticket, AnonymousPlayer::new()));
        }

        Ok(resp)
    }

    fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
        println!("ws:rcv[{}]: user with uuid {} sent ws msg {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 msg);

        let _ = parse_message_ticket(msg)
            .and_then(|t| self.authorizer.authorize_ticket(t))
            .and_then(parse_message_type)
            .and_then(parse_message_action)
            .map(|action| {
                self.out.send(format!("gotcha, you want to {:?}", action))
            })
            .unwrap_or_else(|err| {
                println!("{}", err);
                self.out.send("got your message, but not sure what it meant")
            });

        Ok(())
    }

    fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
        // TODO
        println!("ws:opn[{}]: user with uuid {} opened ws cxn",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]");
        Ok(())
    }

    fn on_close(&mut self, code: ws::CloseCode, reason: &str) {
        println!("ws:cls[{}]: user with uuid {} closed ws cxn\n\tCode [{:?}] reason: {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 code,
                 reason);
        // TODO
    }

    fn on_error(&mut self, err: ws::Error) {
        println!("ws:err[{}]: user with uuid {} got error {}",
                 time::precise_time_ns(),
                 "[[[ we don't have uuids in ws yet ]]]",
                 err);
        // TODO
    }
}

pub fn server (domain: String, port: i32) -> () {
    let connections : ConnectionMap = Arc::new(Mutex::new(HashMap::new()));
    let addr = 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 super::calc_coords;
use crate::shapes::{GeoClass, Scratchpad};
use crate::StrError;
use plotpy::{Canvas, Plot, PolyCode, Text};
use russell_lab::Vector;

fn _draw_edge(canvas: &mut Canvas, edge_pad: &mut Scratchpad) -> Result<(), StrError> {
    const N: usize = 11; // number of points along edge (to handle nonlinear edges)
    let (ksi_min, ksi_del) = (-1.0, 2.0); // all Lin shapes go from -1 to +1
    let space_ndim = edge_pad.xmax.len();
    let mut x = Vector::new(space_ndim);
    canvas.set_face_color("None");
    if space_ndim == 2 {
        canvas.polycurve_begin();
        for i in 0..N {
            let kval = ksi_min + ksi_del * (i as f64) / ((N - 1) as f64);
            calc_coords(&mut x, edge_pad, &[kval])?;
            let code = if i == 0 { PolyCode::MoveTo } else { PolyCode::LineTo };
            canvas.polycurve_add(x[0], x[1], code);
        }
        canvas.polycurve_end(false);
    } else {
        canvas.polyline_3d_begin();
        for i in 0..N {
            let kval = ksi_min + ksi_del * (i as f64) / ((N - 1) as f64);
            calc_coords(&mut x, edge_pad, &[kval])?;
            canvas.polyline_3d_add(x[0], x[1], x[2]);
        }
        canvas.polyline_3d_end();
    }
    Ok(())
}

/// Draws the geometric shape
///
/// # Input
///
/// * `pad` -- the scratchpad with coordinates set already
pub fn draw_shape(plot: &mut Plot, pad: &mut Scratchpad) -> Result<(), StrError> {
    if !pad.ok_xxt {
        return Err("all components of the coordinates matrix must be set first");
    }
    let space_ndim = pad.xmin.len();
    let mut canvas = Canvas::new();
    if pad.kind.ndim() == 1 {
        _draw_edge(&mut canvas, pad)?;
    } else {
        let mut edge_pad = Scratchpad::new(space_ndim, pad.kind.edge_kind().unwrap())?;
        for e in 0..pad.kind.nedge() {
            for i in 0..pad.kind.edge_nnode() {
                let m = pad.kind.edge_node_id(e, i);
                for j in 0..space_ndim {
                    edge_pad.set_xx(i, j, pad.xxt[j][m]);
                }
            }
            _draw_edge(&mut canvas, &mut edge_pad)?;
        }
    }
    let mut labels_corner = Text::new();
    let mut labels_middle = Text::new();
    labels_corner
        .set_align_horizontal("center")
        .set_align_vertical("center")
        .set_bbox(true)
        .set_bbox_style("circle,pad=0.3")
        .set_bbox_facecolor("white")
        .set_bbox_edgecolor("red");
    labels_middle
        .set_align_horizontal("center")
        .set_align_vertical("center")
        .set_fontsize(8.0)
        .set_bbox(true)
        .set_bbox_style("circle,pad=0.2")
        .set_bbox_facecolor("white")
        .set_bbox_edgecolor("gold");
    if space_ndim == 2 {
        let m_corner_max = match pad.kind.class() {
            GeoClass::Lin => 2,
            GeoClass::Tri => 3,
            GeoClass::Qua => 4,
            GeoClass::Tet => 4,
            GeoClass::Hex => 8,
        };
        for m in 0..pad.kind.nnode() {
            if m < m_corner_max {
                labels_corner.draw(pad.xxt[0][m], pad.xxt[1][m], format!("{}", m).as_str());
            } else {
                labels_middle.draw(pad.xxt[0][m], pad.xxt[1][m], format!("{}", m).as_str());
            }
        }
    } else {
        let m_corner_max = if pad.kind.is_tri_or_tet() { 4 } else { 8 };
        for m in 0..pad.kind.nnode() {
            if m < m_corner_max {
                labels_corner.draw_3d(pad.xxt[0][m], pad.xxt[1][m], pad.xxt[2][m], format!("{}", m).as_str());
            } else {
                labels_middle.draw_3d(pad.xxt[0][m], pad.xxt[1][m], pad.xxt[2][m], format!("{}", m).as_str());
            }
        }
    }
    plot.add(&canvas).add(&labels_corner).add(&labels_middle);
    let dx = pad.xmax[0] - pad.xmin[0];
    let dy = pad.xmax[1] - pad.xmin[1];
    const PCT: f64 = 2.0 / 100.0;
    if space_ndim == 2 {
        plot.set_range(
            pad.xmin[0] - dx * PCT,
            pad.xmax[0] + dx * PCT,
            pad.xmin[1] - dy * PCT,
            pad.xmax[1] + dy * PCT,
        );
    } else {
        let dz = pad.xmax[2] - pad.xmin[2];
        plot.set_range_3d(
            pad.xmin[0] - dx * PCT,
            pad.xmax[0] + dx * PCT,
            pad.xmin[1] - dy * PCT,
            pad.xmax[1] + dy * PCT,
            pad.xmin[2] - dz * PCT,
            pad.xmax[2] + dz * PCT,
        );
    }
    Ok(())
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::draw_shape;
    use crate::shapes::op::testing::aux::{_gen_canvas_mapping, gen_scratchpad_with_coords};
    use crate::shapes::GeoKind;
    use crate::StrError;
    use plotpy::Plot;

    // #[test]
    fn _draw_shape_works() -> Result<(), StrError> {
        // select all kinds
        let kinds = vec![
            // Lin
            GeoKind::Lin2,
            GeoKind::Lin3,
            GeoKind::Lin4,
            GeoKind::Lin5,
            // Tri
            GeoKind::Tri3,
            GeoKind::Tri6,
            GeoKind::Tri10,
            GeoKind::Tri15,
            // Qua
            GeoKind::Qua4,
            GeoKind::Qua8,
            GeoKind::Qua9,
            GeoKind::Qua12,
            GeoKind::Qua16,
            GeoKind::Qua17,
            // Tet
            GeoKind::Tet4,
            GeoKind::Tet10,
            GeoKind::Tet20,
            // Hex
            GeoKind::Hex8,
            GeoKind::Hex20,
            GeoKind::Hex32,
        ];
        for kind in kinds {
            let space_ndim = usize::max(2, kind.ndim());
            let mut pad = gen_scratchpad_with_coords(space_ndim, kind);
            let mut plot = Plot::new();
            if space_ndim == 2 {
                let canvas = _gen_canvas_mapping();
                plot.add(&canvas);
                plot.set_range(1.0, 10.0, 1.0, 10.0);
            }
            draw_shape(&mut plot, &mut pad)?;
            if space_ndim == 2 {
                plot.set_range(1.0, 10.0, 1.0, 10.0);
            }
            plot.set_figure_size_points(600.0, 600.0)
                .set_equal_axes(true)
                .save(format!("/tmp/gemlab/test_draw_shape_{}.svg", kind.to_string()).as_str())?;
        }
        Ok(())
    }
}
<commit_msg>Use thicker lines in draw_shape<commit_after>use super::calc_coords;
use crate::shapes::{GeoClass, Scratchpad};
use crate::StrError;
use plotpy::{Canvas, Plot, PolyCode, Text};
use russell_lab::Vector;

fn _draw_edge(canvas: &mut Canvas, edge_pad: &mut Scratchpad) -> Result<(), StrError> {
    const N: usize = 11; // number of points along edge (to handle nonlinear edges)
    let (ksi_min, ksi_del) = (-1.0, 2.0); // all Lin shapes go from -1 to +1
    let space_ndim = edge_pad.xmax.len();
    let mut x = Vector::new(space_ndim);
    canvas.set_face_color("None").set_line_width(3.0);
    if space_ndim == 2 {
        canvas.polycurve_begin();
        for i in 0..N {
            let kval = ksi_min + ksi_del * (i as f64) / ((N - 1) as f64);
            calc_coords(&mut x, edge_pad, &[kval])?;
            let code = if i == 0 { PolyCode::MoveTo } else { PolyCode::LineTo };
            canvas.polycurve_add(x[0], x[1], code);
        }
        canvas.polycurve_end(false);
    } else {
        canvas.polyline_3d_begin();
        for i in 0..N {
            let kval = ksi_min + ksi_del * (i as f64) / ((N - 1) as f64);
            calc_coords(&mut x, edge_pad, &[kval])?;
            canvas.polyline_3d_add(x[0], x[1], x[2]);
        }
        canvas.polyline_3d_end();
    }
    Ok(())
}

/// Draws the geometric shape
///
/// # Input
///
/// * `pad` -- the scratchpad with coordinates set already
pub fn draw_shape(plot: &mut Plot, pad: &mut Scratchpad) -> Result<(), StrError> {
    if !pad.ok_xxt {
        return Err("all components of the coordinates matrix must be set first");
    }
    let space_ndim = pad.xmin.len();
    let mut canvas = Canvas::new();
    if pad.kind.ndim() == 1 {
        _draw_edge(&mut canvas, pad)?;
    } else {
        let mut edge_pad = Scratchpad::new(space_ndim, pad.kind.edge_kind().unwrap())?;
        for e in 0..pad.kind.nedge() {
            for i in 0..pad.kind.edge_nnode() {
                let m = pad.kind.edge_node_id(e, i);
                for j in 0..space_ndim {
                    edge_pad.set_xx(i, j, pad.xxt[j][m]);
                }
            }
            _draw_edge(&mut canvas, &mut edge_pad)?;
        }
    }
    let mut labels_corner = Text::new();
    let mut labels_middle = Text::new();
    labels_corner
        .set_align_horizontal("center")
        .set_align_vertical("center")
        .set_bbox(true)
        .set_bbox_style("circle,pad=0.3")
        .set_bbox_facecolor("white")
        .set_bbox_edgecolor("red");
    labels_middle
        .set_align_horizontal("center")
        .set_align_vertical("center")
        .set_fontsize(8.0)
        .set_bbox(true)
        .set_bbox_style("circle,pad=0.2")
        .set_bbox_facecolor("white")
        .set_bbox_edgecolor("gold");
    if space_ndim == 2 {
        let m_corner_max = match pad.kind.class() {
            GeoClass::Lin => 2,
            GeoClass::Tri => 3,
            GeoClass::Qua => 4,
            GeoClass::Tet => 4,
            GeoClass::Hex => 8,
        };
        for m in 0..pad.kind.nnode() {
            if m < m_corner_max {
                labels_corner.draw(pad.xxt[0][m], pad.xxt[1][m], format!("{}", m).as_str());
            } else {
                labels_middle.draw(pad.xxt[0][m], pad.xxt[1][m], format!("{}", m).as_str());
            }
        }
    } else {
        let m_corner_max = if pad.kind.is_tri_or_tet() { 4 } else { 8 };
        for m in 0..pad.kind.nnode() {
            if m < m_corner_max {
                labels_corner.draw_3d(pad.xxt[0][m], pad.xxt[1][m], pad.xxt[2][m], format!("{}", m).as_str());
            } else {
                labels_middle.draw_3d(pad.xxt[0][m], pad.xxt[1][m], pad.xxt[2][m], format!("{}", m).as_str());
            }
        }
    }
    plot.add(&canvas).add(&labels_corner).add(&labels_middle);
    let dx = pad.xmax[0] - pad.xmin[0];
    let dy = pad.xmax[1] - pad.xmin[1];
    const PCT: f64 = 2.0 / 100.0;
    if space_ndim == 2 {
        plot.set_range(
            pad.xmin[0] - dx * PCT,
            pad.xmax[0] + dx * PCT,
            pad.xmin[1] - dy * PCT,
            pad.xmax[1] + dy * PCT,
        );
    } else {
        let dz = pad.xmax[2] - pad.xmin[2];
        plot.set_range_3d(
            pad.xmin[0] - dx * PCT,
            pad.xmax[0] + dx * PCT,
            pad.xmin[1] - dy * PCT,
            pad.xmax[1] + dy * PCT,
            pad.xmin[2] - dz * PCT,
            pad.xmax[2] + dz * PCT,
        );
    }
    Ok(())
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::draw_shape;
    use crate::shapes::op::testing::aux::{_gen_canvas_mapping, gen_scratchpad_with_coords};
    use crate::shapes::GeoKind;
    use crate::StrError;
    use plotpy::Plot;

    #[test]
    fn _draw_shape_works() -> Result<(), StrError> {
        // select all kinds
        let kinds = vec![
            // Lin
            GeoKind::Lin2,
            GeoKind::Lin3,
            GeoKind::Lin4,
            GeoKind::Lin5,
            // Tri
            GeoKind::Tri3,
            GeoKind::Tri6,
            GeoKind::Tri10,
            GeoKind::Tri15,
            // Qua
            GeoKind::Qua4,
            GeoKind::Qua8,
            GeoKind::Qua9,
            GeoKind::Qua12,
            GeoKind::Qua16,
            GeoKind::Qua17,
            // Tet
            GeoKind::Tet4,
            GeoKind::Tet10,
            GeoKind::Tet20,
            // Hex
            GeoKind::Hex8,
            GeoKind::Hex20,
            GeoKind::Hex32,
        ];
        for kind in kinds {
            let space_ndim = usize::max(2, kind.ndim());
            let mut pad = gen_scratchpad_with_coords(space_ndim, kind);
            let mut plot = Plot::new();
            if space_ndim == 2 {
                let canvas = _gen_canvas_mapping();
                plot.add(&canvas);
                plot.set_range(1.0, 10.0, 1.0, 10.0);
            }
            draw_shape(&mut plot, &mut pad)?;
            if space_ndim == 2 {
                plot.set_range(1.0, 10.0, 1.0, 10.0);
            }
            plot.set_figure_size_points(600.0, 600.0)
                .set_equal_axes(true)
                .save(format!("/tmp/gemlab/test_draw_shape_{}.svg", kind.to_string()).as_str())?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::outlives_bounds::InferCtxtExt as _;
use crate::traits::{self, TraitEngine, TraitEngineExt};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
use rustc_middle::traits::query::Fallible;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::{Span, DUMMY_SP};

use std::fmt::Debug;

pub use rustc_infer::infer::*;

pub trait InferCtxtExt<'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool;

    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>;

    /// Check whether a `ty` implements given trait(trait_def_id).
    /// The inputs are:
    ///
    /// - the def-id of the trait
    /// - the self type
    /// - the *other* type parameters of the trait, excluding the self-type
    /// - the parameter environment
    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool {
        let ty = self.resolve_vars_if_possible(ty);

        if !(param_env, ty).needs_infer() {
            return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
        }

        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);

        // This can get called from typeck (by euv), and `moves_by_default`
        // rightly refuses to work with inference variables, but
        // moves_by_default has a cache, which we want to use in other
        // cases.
        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
    }

    /// Normalizes associated types in `value`, potentially returning
    /// new obligations that must further be processed.
    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("partially_normalize_associated_types_in(value={:?})", value);
        let mut selcx = traits::SelectionContext::new(self);
        let traits::Normalized { value, obligations } =
            traits::normalize(&mut selcx, param_env, cause, value);
        debug!(
            "partially_normalize_associated_types_in: result={:?} predicates={:?}",
            value, obligations
        );
        InferOk { value, obligations }
    }

    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult {
        debug!(
            "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
            trait_def_id, ty, params, param_env
        );

        let trait_ref =
            ty::TraitRef { def_id: trait_def_id, substs: self.tcx.mk_substs_trait(ty, params) };

        let obligation = traits::Obligation {
            cause: traits::ObligationCause::dummy(),
            param_env,
            recursion_depth: 0,
            predicate: trait_ref.without_const().to_predicate(self.tcx),
        };
        self.evaluate_obligation_no_overflow(&obligation)
    }
}

pub trait InferCtxtBuilderExt<'tcx> {
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}

impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
    /// The "main method" for a canonicalized trait query. Given the
    /// canonical key `canonical_key`, this method will create a new
    /// inference context, instantiate the key, and run your operation
    /// `op`. The operation should yield up a result (of type `R`) as
    /// well as a set of trait obligations that must be fully
    /// satisfied. These obligations will be processed and the
    /// canonical result created.
    ///
    /// Returns `NoSolution` in the event of any error.
    ///
    /// (It might be mildly nicer to implement this on `TyCtxt`, and
    /// not `InferCtxtBuilder`, but that is a bit tricky right now.
    /// In part because we would need a `for<'tcx>` sort of
    /// bound for the closure and in part because it is convenient to
    /// have `'tcx` be free on this function so that we can talk about
    /// `K: TypeFoldable<'tcx>`.)
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
    {
        self.enter_with_canonical(
            DUMMY_SP,
            canonical_key,
            |ref infcx, key, canonical_inference_vars| {
                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
                let value = operation(infcx, &mut *fulfill_cx, key)?;
                infcx.make_canonicalized_query_response(
                    canonical_inference_vars,
                    value,
                    &mut *fulfill_cx,
                )
            },
        )
    }
}

pub trait OutlivesEnvironmentExt<'tcx> {
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    );
}

impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
    /// This method adds "implied bounds" into the outlives environment.
    /// Implied bounds are outlives relationships that we can deduce
    /// on the basis that certain types must be well-formed -- these are
    /// either the types that appear in the function signature or else
    /// the input types to an impl. For example, if you have a function
    /// like
    ///
    /// ```
    /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
    /// ```
    ///
    /// we can assume in the caller's body that `'b: 'a` and that `T:
    /// 'b` (and hence, transitively, that `T: 'a`). This method would
    /// add those assumptions into the outlives-environment.
    ///
    /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs`
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    ) {
        debug!("add_implied_bounds()");

        for &ty in fn_sig_tys {
            let ty = infcx.resolve_vars_if_possible(ty);
            debug!("add_implied_bounds: ty = {}", ty);
            let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
            self.add_outlives_bounds(Some(infcx), implied_bounds)
        }
    }
}
<commit_msg>type_implements_trait consider obligation failure on overflow<commit_after>use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::outlives_bounds::InferCtxtExt as _;
use crate::traits::{self, TraitEngine, TraitEngineExt};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
use rustc_middle::traits::query::Fallible;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::{Span, DUMMY_SP};

use std::fmt::Debug;

pub use rustc_infer::infer::*;

pub trait InferCtxtExt<'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool;

    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>;

    /// Check whether a `ty` implements given trait(trait_def_id).
    /// The inputs are:
    ///
    /// - the def-id of the trait
    /// - the self type
    /// - the *other* type parameters of the trait, excluding the self-type
    /// - the parameter environment
    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool {
        let ty = self.resolve_vars_if_possible(ty);

        if !(param_env, ty).needs_infer() {
            return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
        }

        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);

        // This can get called from typeck (by euv), and `moves_by_default`
        // rightly refuses to work with inference variables, but
        // moves_by_default has a cache, which we want to use in other
        // cases.
        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
    }

    /// Normalizes associated types in `value`, potentially returning
    /// new obligations that must further be processed.
    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("partially_normalize_associated_types_in(value={:?})", value);
        let mut selcx = traits::SelectionContext::new(self);
        let traits::Normalized { value, obligations } =
            traits::normalize(&mut selcx, param_env, cause, value);
        debug!(
            "partially_normalize_associated_types_in: result={:?} predicates={:?}",
            value, obligations
        );
        InferOk { value, obligations }
    }

    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult {
        debug!(
            "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
            trait_def_id, ty, params, param_env
        );

        let trait_ref =
            ty::TraitRef { def_id: trait_def_id, substs: self.tcx.mk_substs_trait(ty, params) };

        let obligation = traits::Obligation {
            cause: traits::ObligationCause::dummy(),
            param_env,
            recursion_depth: 0,
            predicate: trait_ref.without_const().to_predicate(self.tcx),
        };
        self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
    }
}

pub trait InferCtxtBuilderExt<'tcx> {
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}

impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
    /// The "main method" for a canonicalized trait query. Given the
    /// canonical key `canonical_key`, this method will create a new
    /// inference context, instantiate the key, and run your operation
    /// `op`. The operation should yield up a result (of type `R`) as
    /// well as a set of trait obligations that must be fully
    /// satisfied. These obligations will be processed and the
    /// canonical result created.
    ///
    /// Returns `NoSolution` in the event of any error.
    ///
    /// (It might be mildly nicer to implement this on `TyCtxt`, and
    /// not `InferCtxtBuilder`, but that is a bit tricky right now.
    /// In part because we would need a `for<'tcx>` sort of
    /// bound for the closure and in part because it is convenient to
    /// have `'tcx` be free on this function so that we can talk about
    /// `K: TypeFoldable<'tcx>`.)
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
    {
        self.enter_with_canonical(
            DUMMY_SP,
            canonical_key,
            |ref infcx, key, canonical_inference_vars| {
                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
                let value = operation(infcx, &mut *fulfill_cx, key)?;
                infcx.make_canonicalized_query_response(
                    canonical_inference_vars,
                    value,
                    &mut *fulfill_cx,
                )
            },
        )
    }
}

pub trait OutlivesEnvironmentExt<'tcx> {
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    );
}

impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
    /// This method adds "implied bounds" into the outlives environment.
    /// Implied bounds are outlives relationships that we can deduce
    /// on the basis that certain types must be well-formed -- these are
    /// either the types that appear in the function signature or else
    /// the input types to an impl. For example, if you have a function
    /// like
    ///
    /// ```
    /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
    /// ```
    ///
    /// we can assume in the caller's body that `'b: 'a` and that `T:
    /// 'b` (and hence, transitively, that `T: 'a`). This method would
    /// add those assumptions into the outlives-environment.
    ///
    /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs`
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    ) {
        debug!("add_implied_bounds()");

        for &ty in fn_sig_tys {
            let ty = infcx.resolve_vars_if_possible(ty);
            debug!("add_implied_bounds: ty = {}", ty);
            let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
            self.add_outlives_bounds(Some(infcx), implied_bounds)
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;

use nom::bytes::complete::tag;
use nom::character::complete::multispace1;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;

#[derive(Debug)]
struct BingoCard([(bool, u8); 25]);

impl BingoCard {
    pub fn cross(&mut self, num: u8) -> bool {
        if let Some(card) = self.0.iter_mut().find(|&&mut (_, x)| x == num) {
            card.0 = true;
            true
        } else {
            false
        }
    }

    pub fn has_won(&self) -> bool {
        // Check horizontal lines
        if self.0.chunks_exact(5).any(|s| s.iter().all(|&b| b.0)) {
            return true;
        }

        // Check vertical lines
        (0..5).any(|x| self.0.iter().skip(x).step_by(5).all(|b| b.0))

        // Diagonals do not count
    }

    pub fn remaining(&self) -> u32 {
        self.0
            .iter()
            .filter_map(|&(ticked, num)| if !ticked { Some(num as u32) } else { None })
            .sum()
    }
}

fn parse_numbers(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
    use nom::character::complete::u8;
    separated_list1(tag(","), u8)(input)
}

fn parse_bingo(mut input: &[u8]) -> IResult<&[u8], BingoCard> {
    use nom::character::complete::u8;

    let mut card = [0; 25];

    let mut parse_num = preceded(multispace1, u8);

    // fill doesn't work with preceded
    for i in 0..25 {
        let result = parse_num(input)?;
        card[i] = result.1;
        input = result.0;
    }

    Ok((input, BingoCard(card.map(|x| (false, x)))))
}

fn parse_input(input: &[u8]) -> IResult<&[u8], (Vec<u8>, Vec<BingoCard>)> {
    tuple((parse_numbers, many1(parse_bingo)))(input)
}

fn read_input(input: &mut dyn Read) -> (Vec<u8>, Vec<BingoCard>) {
    let mut buffer = Vec::new();

    input.read_to_end(&mut buffer).unwrap();

    parse_input(&buffer).finish().unwrap().1
}

pub fn part1(input: &mut dyn Read) -> String {
    let (numbers, mut bingo_cards) = read_input(input);

    for number in numbers {
        for card in &mut bingo_cards {
            if card.cross(number) && card.has_won() {
                return (number as u32 * card.remaining()).to_string();
            }
        }
    }

    panic!("None of the cards won")
}

pub fn part2(input: &mut dyn Read) -> String {
    let (numbers, mut bingo_cards) = read_input(input);
    let mut bingo_won = vec![false; bingo_cards.len()];
    let mut num_won = 0;
    let to_win = bingo_cards.len();

    for num in numbers {
        for (won, card) in bingo_won.iter_mut().zip(bingo_cards.iter_mut()) {
            if !*won && card.cross(num) && card.has_won() {
                *won = true;
                num_won += 1;

                if num_won == to_win {
                    return (num as u32 * card.remaining()).to_string();
                }
            }
        }
    }

    panic!("Not all cards won!")
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/04.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 4512)
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 1924)
    }
}
<commit_msg>Improve "winning" logic<commit_after>use std::io::Read;

use nom::bytes::complete::tag;
use nom::character::complete::multispace1;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::Finish;
use nom::IResult;

#[derive(Debug)]
struct BingoCard([(bool, u8); 25]);

impl BingoCard {
    pub fn cross(&mut self, num: u8) -> Option<usize> {
        self.0
            .iter_mut()
            .enumerate()
            .find_map(|(pos, (ticked, x))| {
                if *x == num {
                    *ticked = true;
                    Some(pos)
                } else {
                    None
                }
            })
    }

    pub fn has_won(&self, crossed: usize) -> bool {
        // Check horizontal lines
        if self
            .0
            .chunks_exact(5)
            .nth(crossed / 5)
            .unwrap()
            .iter()
            .all(|&b| b.0)
        {
            return true;
        }

        // Check vertical lines
        self.0.iter().skip(crossed % 5).step_by(5).all(|b| b.0)

        // Diagonals do not count
    }

    pub fn remaining(&self) -> u32 {
        self.0
            .iter()
            .filter_map(|&(ticked, num)| if !ticked { Some(num as u32) } else { None })
            .sum()
    }
}

fn parse_numbers(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
    use nom::character::complete::u8;
    separated_list1(tag(","), u8)(input)
}

fn parse_bingo(mut input: &[u8]) -> IResult<&[u8], BingoCard> {
    use nom::character::complete::u8;

    let mut card = [0; 25];

    let mut parse_num = preceded(multispace1, u8);

    // fill doesn't work with preceded
    for i in 0..25 {
        let result = parse_num(input)?;
        card[i] = result.1;
        input = result.0;
    }

    Ok((input, BingoCard(card.map(|x| (false, x)))))
}

fn parse_input(input: &[u8]) -> IResult<&[u8], (Vec<u8>, Vec<BingoCard>)> {
    tuple((parse_numbers, many1(parse_bingo)))(input)
}

fn read_input(input: &mut dyn Read) -> (Vec<u8>, Vec<BingoCard>) {
    let mut buffer = Vec::new();

    input.read_to_end(&mut buffer).unwrap();

    parse_input(&buffer).finish().unwrap().1
}

pub fn part1(input: &mut dyn Read) -> String {
    let (numbers, mut bingo_cards) = read_input(input);

    for number in numbers {
        for card in &mut bingo_cards {
            if matches!(card.cross(number), Some(pos) if card.has_won(pos)) {
                return (number as u32 * card.remaining()).to_string();
            }
        }
    }

    panic!("None of the cards won")
}

pub fn part2(input: &mut dyn Read) -> String {
    let (numbers, mut bingo_cards) = read_input(input);
    let mut bingo_won = vec![false; bingo_cards.len()];
    let mut num_won = 0;
    let to_win = bingo_cards.len();

    for num in numbers {
        for (won, card) in bingo_won.iter_mut().zip(bingo_cards.iter_mut()) {
            if *won {
                continue;
            }

            if matches!(card.cross(num), Some(pos) if card.has_won(pos)) {
                *won = true;
                num_won += 1;

                if num_won == to_win {
                    return (num as u32 * card.remaining()).to_string();
                }
            }
        }
    }

    panic!("Not all cards won!")
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE: &[u8] = include_bytes!("samples/04.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 4512)
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 1924)
    }
}
<|endoftext|>


<commit_before>/// Async server-side trees

use std::{ops, fmt, mem};
use dbus::tree::{Factory, Tree, MethodType, DataType, MTFn, Method, MethodInfo, MethodErr};
use dbus::{Member, Message, Connection};
use std::marker::PhantomData;
use std::cell::RefCell;
use futures::{IntoFuture, Future, Poll, Stream, Async};
use std::rc::Rc;
use super::AMessageStream;
use std::ffi::CString;

pub trait ADataType: fmt::Debug + Sized + Default {
    type ObjectPath: fmt::Debug;
    type Property: fmt::Debug;
    type Interface: fmt::Debug + Default;
    type Method: fmt::Debug + Default;
    type Signal: fmt::Debug;
}

#[derive(Debug, Default)]
pub struct ATree<D: ADataType>(RefCell<Vec<AMethodResult>>, PhantomData<*const D>);

impl<D: ADataType> ATree<D> {
    pub fn new() -> Self { Default::default() }
    fn push(&self, a: AMethodResult) { self.0.borrow_mut().push(a); }
}

impl<D: ADataType> DataType for ATree<D> {
    type Tree = ATree<D>;
    type ObjectPath = D::ObjectPath;
    type Property = D::Property;
    type Interface = D::Interface;
    type Method = D::Method;
    type Signal = D::Signal;
}

impl ADataType for () {
    type ObjectPath = ();
    type Property = ();
    type Interface = ();
    type Method = ();
    type Signal = ();
}

/// A Tree factory that allows both synchronous and asynchronous methods.
pub struct AFactory<M: MethodType<D>, D: DataType = ()>(Factory<M, D>);

impl AFactory<MTFn<()>, ()> {
    pub fn new_afn<D: ADataType>() -> AFactory<MTFn<ATree<D>>, ATree<D>> { AFactory(Factory::new_fn()) }
}

impl<M: MethodType<D>, D: DataType> ops::Deref for AFactory<M, D> {
    type Target = Factory<M, D>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl<D: ADataType> AFactory<MTFn<ATree<D>>, ATree<D>> {
    /// Creates an async method, for methods whose result cannot be returned immediately.
    ///
    /// The method handler supplied to amethod returns a future, which resolves into the method result.
    /// See the tokio_server example for some hints on how to use it.
    pub fn amethod<H, R, T>(&self, t: T, data: D::Method, handler: H) -> Method<MTFn<ATree<D>>, ATree<D>>
    where H: 'static + Fn(&MethodInfo<MTFn<ATree<D>>, ATree<D>>) -> R, T: Into<Member<'static>>,
        R: 'static + IntoFuture<Item=Vec<Message>, Error=MethodErr> {
        self.0.method(t, data, move |minfo| {
            let r = handler(minfo);
            minfo.tree.get_data().push(AMethodResult::new(r));
            Ok(Vec::new())
        })
    }
}

/// A Future method result
///
/// When method results cannot be returned right away, the AMethodResult holds it temporarily
struct AMethodResult(Box<Future<Item=Vec<Message>, Error=MethodErr>>, Option<Message>);

impl fmt::Debug for AMethodResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "AMethodResult({:?})", self.1) }
}

impl AMethodResult {
    fn new<F: 'static + IntoFuture<Item=Vec<Message>, Error=MethodErr>>(f: F) -> Self {
        AMethodResult(Box::new(f.into_future()), None)
    }
}

impl Future for AMethodResult {
    type Item = Vec<Message>;
    type Error = MethodErr;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

#[derive(Debug)]
/// Creates a filter for incoming messages, that handles messages in the tree.
///
/// See the tokio_server example for some hints on how to use it.
pub struct ATreeServer<'a, D: ADataType + 'a> {
   conn: Rc<Connection>,
   tree: &'a Tree<MTFn<ATree<D>>, ATree<D>>,
   stream: AMessageStream,
   pendingresults: Vec<AMethodResult>,
}

impl<'a, D: ADataType> ATreeServer<'a, D> {
    pub fn new(c: Rc<Connection>, t: &'a Tree<MTFn<ATree<D>>, ATree<D>>, stream: AMessageStream) -> Self {
        ATreeServer { conn: c, tree: t, stream: stream, pendingresults: vec![] }
    }

    fn spawn_method_results(&mut self, msg: Message) {
        let v = mem::replace(&mut *self.tree.get_data().0.borrow_mut(), vec![]);
        let mut msg = Some(msg);
        for mut r in v {
            if r.1.is_none() { r.1 = msg.take() };
            // println!("Pushing {:?}", r);
            self.pendingresults.push(r);
        }
    }

    fn check_pending_results(&mut self) {
        let v = mem::replace(&mut self.pendingresults, vec!());
        self.pendingresults = v.into_iter().filter_map(|mut mr| {
            let z = mr.poll();
            // println!("Polling {:?} returned {:?}", mr, z);
            match z {
                Ok(Async::NotReady) => Some(mr),
                Ok(Async::Ready(t)) => { for msg in t { self.conn.send(msg).expect("D-Bus send error"); }; None },
                Err(e) => {
                    let m = mr.1.take().unwrap();
                    let msg = m.error(&e.errorname(), &CString::new(e.description()).unwrap());
                    self.conn.send(msg).expect("D-Bus send error");
                    None
                }
            }
        }).collect();
    }
}

impl<'a, D: ADataType> Stream for ATreeServer<'a, D> {
    type Item = Message;
    type Error = ();
    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            self.check_pending_results();
            let z = self.stream.poll();
            if let Ok(Async::Ready(Some(m))) = z {
                // println!("treeserver {:?}", m);
                let hh = self.tree.handle(&m);
                // println!("hh: {:?}", hh);
                if let Some(v) = hh {
                    self.spawn_method_results(m);
                    for msg in v { self.conn.send(msg)?; }
                    // We consumed the message. Poll again
                } else { return Ok(Async::Ready(Some(m))) }
            } else { return z }
        }
    }
}

<commit_msg>Tokio: Make it possible to chain ATreeServer<commit_after>/// Async server-side trees

use std::{ops, fmt, mem};
use dbus::tree::{Factory, Tree, MethodType, DataType, MTFn, Method, MethodInfo, MethodErr};
use dbus::{Member, Message, Connection};
use std::marker::PhantomData;
use std::cell::RefCell;
use futures::{IntoFuture, Future, Poll, Stream, Async};
use std::rc::Rc;
use std::ffi::CString;

pub trait ADataType: fmt::Debug + Sized + Default {
    type ObjectPath: fmt::Debug;
    type Property: fmt::Debug;
    type Interface: fmt::Debug + Default;
    type Method: fmt::Debug + Default;
    type Signal: fmt::Debug;
}

#[derive(Debug, Default)]
pub struct ATree<D: ADataType>(RefCell<Vec<AMethodResult>>, PhantomData<*const D>);

impl<D: ADataType> ATree<D> {
    pub fn new() -> Self { Default::default() }
    fn push(&self, a: AMethodResult) { self.0.borrow_mut().push(a); }
}

impl<D: ADataType> DataType for ATree<D> {
    type Tree = ATree<D>;
    type ObjectPath = D::ObjectPath;
    type Property = D::Property;
    type Interface = D::Interface;
    type Method = D::Method;
    type Signal = D::Signal;
}

impl ADataType for () {
    type ObjectPath = ();
    type Property = ();
    type Interface = ();
    type Method = ();
    type Signal = ();
}

/// A Tree factory that allows both synchronous and asynchronous methods.
pub struct AFactory<M: MethodType<D>, D: DataType = ()>(Factory<M, D>);

impl AFactory<MTFn<()>, ()> {
    pub fn new_afn<D: ADataType>() -> AFactory<MTFn<ATree<D>>, ATree<D>> { AFactory(Factory::new_fn()) }
}

impl<M: MethodType<D>, D: DataType> ops::Deref for AFactory<M, D> {
    type Target = Factory<M, D>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl<D: ADataType> AFactory<MTFn<ATree<D>>, ATree<D>> {
    /// Creates an async method, for methods whose result cannot be returned immediately.
    ///
    /// The method handler supplied to amethod returns a future, which resolves into the method result.
    /// See the tokio_server example for some hints on how to use it.
    pub fn amethod<H, R, T>(&self, t: T, data: D::Method, handler: H) -> Method<MTFn<ATree<D>>, ATree<D>>
    where H: 'static + Fn(&MethodInfo<MTFn<ATree<D>>, ATree<D>>) -> R, T: Into<Member<'static>>,
        R: 'static + IntoFuture<Item=Vec<Message>, Error=MethodErr> {
        self.0.method(t, data, move |minfo| {
            let r = handler(minfo);
            minfo.tree.get_data().push(AMethodResult::new(r));
            Ok(Vec::new())
        })
    }
}

/// A Future method result
///
/// When method results cannot be returned right away, the AMethodResult holds it temporarily
struct AMethodResult(Box<Future<Item=Vec<Message>, Error=MethodErr>>, Option<Message>);

impl fmt::Debug for AMethodResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "AMethodResult({:?})", self.1) }
}

impl AMethodResult {
    fn new<F: 'static + IntoFuture<Item=Vec<Message>, Error=MethodErr>>(f: F) -> Self {
        AMethodResult(Box::new(f.into_future()), None)
    }
}

impl Future for AMethodResult {
    type Item = Vec<Message>;
    type Error = MethodErr;
    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

#[derive(Debug)]
/// Creates a filter for incoming messages, that handles messages in the tree.
///
/// See the tokio_server example for some hints on how to use it.
pub struct ATreeServer<'a, D: ADataType + 'a, S> {
   conn: Rc<Connection>,
   tree: &'a Tree<MTFn<ATree<D>>, ATree<D>>,
   stream: S,
   pendingresults: Vec<AMethodResult>,
}

impl<'a, D: ADataType, S: Stream<Item=Message, Error=()>> ATreeServer<'a, D, S> {
    pub fn new(c: Rc<Connection>, t: &'a Tree<MTFn<ATree<D>>, ATree<D>>, stream: S) -> Self {
        ATreeServer { conn: c, tree: t, stream: stream, pendingresults: vec![] }
    }

    fn spawn_method_results(&mut self, msg: Message) {
        let v = mem::replace(&mut *self.tree.get_data().0.borrow_mut(), vec![]);
        let mut msg = Some(msg);
        for mut r in v {
            if r.1.is_none() { r.1 = msg.take() };
            // println!("Pushing {:?}", r);
            self.pendingresults.push(r);
        }
    }

    fn check_pending_results(&mut self) {
        let v = mem::replace(&mut self.pendingresults, vec!());
        self.pendingresults = v.into_iter().filter_map(|mut mr| {
            let z = mr.poll();
            // println!("Polling {:?} returned {:?}", mr, z);
            match z {
                Ok(Async::NotReady) => Some(mr),
                Ok(Async::Ready(t)) => { for msg in t { self.conn.send(msg).expect("D-Bus send error"); }; None },
                Err(e) => {
                    let m = mr.1.take().unwrap();
                    let msg = m.error(&e.errorname(), &CString::new(e.description()).unwrap());
                    self.conn.send(msg).expect("D-Bus send error");
                    None
                }
            }
        }).collect();
    }
}

impl<'a, D: ADataType, S: Stream<Item=Message, Error=()>> Stream for ATreeServer<'a, D, S> {
    type Item = Message;
    type Error = ();
    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        loop {
            self.check_pending_results();
            let z = self.stream.poll();
            if let Ok(Async::Ready(Some(m))) = z {
                // println!("treeserver {:?}", m);
                let hh = self.tree.handle(&m);
                // println!("hh: {:?}", hh);
                if let Some(v) = hh {
                    self.spawn_method_results(m);
                    for msg in v { self.conn.send(msg)?; }
                    // We consumed the message. Poll again
                } else { return Ok(Async::Ready(Some(m))) }
            } else { return z }
        }
    }
}

<|endoftext|>


<commit_before>/*!

Low-level bindings

*/

use libc::{c_char, c_int, c_void};
use core::libc::types::common::c99::{uint32_t, int32_t, uint16_t};

type c_enum = c_int;
type rust_enum = uint;

pub mod error {
    pub type sdl_error_flag = c_enum;

    pub const SDL_ENOMEM: sdl_error_flag      = 0;
    pub const SDL_EFREAD: sdl_error_flag      = 1;
    pub const SDL_EFWRITE: sdl_error_flag     = 2;
    pub const SDL_EFSEEK: sdl_error_flag      = 3;
    pub const SDL_UNSUPPORTED: sdl_error_flag = 4;

    extern {
        fn SDL_Error(code: sdl_error_flag);
        // FIXME: This is actually a varargs call
        fn SDL_SetError(fmt: *c_char);
        fn SDL_GetError() -> *c_char;
        fn SDL_ClearError();
    }
}

pub mod sdl {

    pub type sdl_init_flag = uint32_t;

    pub const SDL_INIT_TIMER: sdl_init_flag         = 0x00000001;
    pub const SDL_INIT_AUDIO: sdl_init_flag         = 0x00000010;
    pub const SDL_INIT_VIDEO: sdl_init_flag         = 0x00000020;
    pub const SDL_INIT_CDROM: sdl_init_flag         = 0x00000100;
    pub const SDL_INIT_JOYSTICK: sdl_init_flag      = 0x00000200;
    pub const SDL_INIT_HAPTIC: sdl_init_flag        = 0x00001000;
    pub const SDL_INIT_NO_PARACHUTE: sdl_init_flag  = 0x00100000;
    pub const SDL_INIT_EVENTTHREAD: sdl_init_flag   = 0x01000000;
    pub const SDL_INIT_EVERYTHING: sdl_init_flag    = 0x0000FFFF;

    extern mod SDL{ // Note: Rust chokes if this is unspecified, whereas it's fine elsewhere. Go figure.
        fn SDL_Init(flags: sdl_init_flag) -> c_int;
        fn SDL_InitSubSystem(flags: sdl_init_flag) -> c_int;
        fn SDL_QuitSubSystem(flags: sdl_init_flag);
        fn SDL_Quit();
        fn SDL_WasInit(flags: sdl_init_flag) -> sdl_init_flag;
    }
}

/*pub mod event {

    extern {
        fn SDL_PollEvent(event: *RawEvent) -> int32_t;
    }
}

pub mod keyboard {
}
*/
pub mod video {

    type RWOps = c_void;

    pub type Surface = {
        flags: sdl_flag,
        format: *c_void,
        w: c_int,
        h: c_int,
        pitch: uint16_t,
        pixels: *c_void,
        offset: c_int
        // FIXME: Remaining are unlisted
    };

    pub type sdl_flag = uint32_t;
    pub type sdl_surface_flag = sdl_flag;

    pub const SDL_SWSURFACE: sdl_surface_flag = 0x00000000;
    pub const SDL_HWSURFACE: sdl_surface_flag = 0x00000001;
    pub const SDL_ASYNCBLIT: sdl_surface_flag = 0x00000004;

    pub type sdl_video_mode_flag = sdl_flag;

    pub const SDL_ANYFORMAT: sdl_video_mode_flag  = 0x10000000;
    pub const SDL_HWPALETTE: sdl_video_mode_flag  = 0x20000000;
    pub const SDL_DOUBLEBUF: sdl_video_mode_flag  = 0x40000000;
    pub const SDL_FULLSCREEN: sdl_video_mode_flag = 0x80000000;
    pub const SDL_OPENGL: sdl_video_mode_flag     = 0x00000002;
    pub const SDL_OPENGLBLIT: sdl_video_mode_flag = 0x0000000A;
    pub const SDL_RESIZABLE: sdl_video_mode_flag  = 0x00000010;
    pub const SDL_NOFRAME: sdl_video_mode_flag    = 0x00000020;

    extern {
        fn SDL_SetVideoMode(width: c_int, height: c_int, bitsperpixel: c_int, flags: sdl_flag) -> *Surface;
        fn SDL_FreeSurface(surface: *Surface);
        fn SDL_LoadBMP_RW(src: *RWOps, freesrc: c_int) -> *Surface;
        fn SDL_RWFromFile(file: *c_char, mode: *c_char) -> *RWOps;
        fn SDL_DisplayFormat(surface: *Surface) -> *Surface;
        fn SDL_UpperBlit(src: *Surface, srcrect: *util::Rect,
                         dst: *Surface, dstrect: *util::Rect) -> c_int;
        fn SDL_Flip(screen: *Surface) -> c_int;
        fn SDL_CreateRGBSurface(flags: sdl_flag, width: c_int, height: c_int,
                                bitsPerPixel: c_int,
                                Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) -> *Surface;
        fn SDL_FillRect(dst: *Surface, dstrect: *util::Rect, color: uint32_t) -> c_int;
        fn SDL_LockSurface(surface: *Surface) -> c_int;
        fn SDL_UnlockSurface(surface: *Surface) -> c_int;
    }
}
<commit_msg>Started on Events<commit_after>/*!

Low-level bindings

*/

use libc::{c_char, c_int, c_void};
use core::libc::types::common::c99::{uint32_t, int32_t, uint16_t};

type c_enum = c_int;
type rust_enum = uint;

pub mod error {
    pub type sdl_error_flag = c_enum;

    pub const SDL_ENOMEM: sdl_error_flag      = 0;
    pub const SDL_EFREAD: sdl_error_flag      = 1;
    pub const SDL_EFWRITE: sdl_error_flag     = 2;
    pub const SDL_EFSEEK: sdl_error_flag      = 3;
    pub const SDL_UNSUPPORTED: sdl_error_flag = 4;

    extern {
        fn SDL_Error(code: sdl_error_flag);
        // FIXME: This is actually a varargs call
        fn SDL_SetError(fmt: *c_char);
        fn SDL_GetError() -> *c_char;
        fn SDL_ClearError();
    }
}

pub mod sdl {

    pub type sdl_init_flag = uint32_t;

    pub const SDL_INIT_TIMER: sdl_init_flag         = 0x00000001;
    pub const SDL_INIT_AUDIO: sdl_init_flag         = 0x00000010;
    pub const SDL_INIT_VIDEO: sdl_init_flag         = 0x00000020;
    pub const SDL_INIT_CDROM: sdl_init_flag         = 0x00000100;
    pub const SDL_INIT_JOYSTICK: sdl_init_flag      = 0x00000200;
    pub const SDL_INIT_HAPTIC: sdl_init_flag        = 0x00001000;
    pub const SDL_INIT_NO_PARACHUTE: sdl_init_flag  = 0x00100000;
    pub const SDL_INIT_EVENTTHREAD: sdl_init_flag   = 0x01000000;
    pub const SDL_INIT_EVERYTHING: sdl_init_flag    = 0x0000FFFF;

    extern mod SDL{ // Note: Rust chokes if this is unspecified, whereas it's fine elsewhere. Go figure.
        fn SDL_Init(flags: sdl_init_flag) -> c_int;
        fn SDL_InitSubSystem(flags: sdl_init_flag) -> c_int;
        fn SDL_QuitSubSystem(flags: sdl_init_flag);
        fn SDL_Quit();
        fn SDL_WasInit(flags: sdl_init_flag) -> sdl_init_flag;
    }
}

pub mod event {

    type sdl_event_type = u8; //FIXME: Should this be c_enum?

    const SDL_NOEVENT: sdl_event_type = 0u8;
    const SDL_ACTIVEEVENT: sdl_event_type = 1u8;
    const SDL_KEYDOWN: sdl_event_type = 2u8;
    const SDL_KEYUP: sdl_event_type = 3u8;
    const SDL_MOUSEMOTION: sdl_event_type = 4u8;
    const SDL_MOUSEBUTTONDown: sdl_event_type = 5u8;
    const SDL_MOUSEBUTTONUP: sdl_event_type = 6u8;
    const SDL_JOYAXISMOTION: sdl_event_type = 7u8;
    const SDL_JOYBALLMOTION: sdl_event_type = 8u8;
    const SDL_JOYHATMOTION: sdl_event_type = 9u8;
    const SDL_JOYBUTTONDOWN: sdl_event_type = 10u8;
    const SDL_JOYBUTTONUP: sdl_event_type = 11u8;
    const SDL_QUIT: sdl_event_type = 12u8;
    const SDL_SYSWMEVENT: sdl_event_type = 13u8;
    const SDL_EVENT_RESERVEDA: sdl_event_type = 14u8;
    const SDL_EVENT_RESERVEDB: sdl_event_type = 15u8;
    const SDL_VIDEORESIZE: sdl_event_type = 16u8;
    const SDL_VIDEOEXPOSE: sdl_event_type = 17u8;
    const SDL_EVENT_RESERVED2: sdl_event_type = 18u8;
    const SDL_EVENT_RESERVED3: sdl_event_type = 19u8;
    const SDL_EVENT_RESERVED4: sdl_event_type = 20u8;
    const SDL_EVENT_RESERVED5: sdl_event_type = 21u8;
    const SDL_EVENT_RESERVED6: sdl_event_type = 22u8;
    const SDL_EVENT_RESERVED7: sdl_event_type = 23u8;
    const SDL_EVENT_USEREVENT: sdl_event_type = 24u8;

    extern {
        fn SDL_PollEvent(event: *RawEvent) -> c_int;
    }
}
/*
pub mod keyboard {
}
*/
pub mod video {

    type RWOps = c_void;

    pub type Surface = {
        flags: sdl_flag,
        format: *c_void,
        w: c_int,
        h: c_int,
        pitch: uint16_t,
        pixels: *c_void,
        offset: c_int
        // FIXME: Remaining are unlisted
    };

    pub type sdl_flag = uint32_t;
    pub type sdl_surface_flag = sdl_flag;

    pub const SDL_SWSURFACE: sdl_surface_flag = 0x00000000;
    pub const SDL_HWSURFACE: sdl_surface_flag = 0x00000001;
    pub const SDL_ASYNCBLIT: sdl_surface_flag = 0x00000004;

    pub type sdl_video_mode_flag = sdl_flag;

    pub const SDL_ANYFORMAT: sdl_video_mode_flag  = 0x10000000;
    pub const SDL_HWPALETTE: sdl_video_mode_flag  = 0x20000000;
    pub const SDL_DOUBLEBUF: sdl_video_mode_flag  = 0x40000000;
    pub const SDL_FULLSCREEN: sdl_video_mode_flag = 0x80000000;
    pub const SDL_OPENGL: sdl_video_mode_flag     = 0x00000002;
    pub const SDL_OPENGLBLIT: sdl_video_mode_flag = 0x0000000A;
    pub const SDL_RESIZABLE: sdl_video_mode_flag  = 0x00000010;
    pub const SDL_NOFRAME: sdl_video_mode_flag    = 0x00000020;

    extern {
        fn SDL_SetVideoMode(width: c_int, height: c_int, bitsperpixel: c_int, flags: sdl_flag) -> *Surface;
        fn SDL_FreeSurface(surface: *Surface);
        fn SDL_LoadBMP_RW(src: *RWOps, freesrc: c_int) -> *Surface;
        fn SDL_RWFromFile(file: *c_char, mode: *c_char) -> *RWOps;
        fn SDL_DisplayFormat(surface: *Surface) -> *Surface;
        fn SDL_UpperBlit(src: *Surface, srcrect: *util::Rect,
                         dst: *Surface, dstrect: *util::Rect) -> c_int;
        fn SDL_Flip(screen: *Surface) -> c_int;
        fn SDL_CreateRGBSurface(flags: sdl_flag, width: c_int, height: c_int,
                                bitsPerPixel: c_int,
                                Rmask: uint32_t, Gmask: uint32_t, Bmask: uint32_t, Amask: uint32_t) -> *Surface;
        fn SDL_FillRect(dst: *Surface, dstrect: *util::Rect, color: uint32_t) -> c_int;
        fn SDL_LockSurface(surface: *Surface) -> c_int;
        fn SDL_UnlockSurface(surface: *Surface) -> c_int;
    }
}
<|endoftext|>


<commit_before>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let skip_copyright = contents.contains("ignore-tidy-copyright");
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 | 2 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<commit_msg>Disallow double trailing newlines in tidy<commit_after>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let skip_copyright = contents.contains("ignore-tidy-copyright");
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<|endoftext|>