<commit_before>use rlua::{self, Lua, Value, ToLua, FromLua};

pub type PropF<'lua> = rlua::Function<'lua>;

pub struct Property<'lua> {
    pub name: String,
    pub cb_new: Option<PropF<'lua>>,
    pub cb_index: Option<PropF<'lua>>,
    pub cb_newindex: Option<PropF<'lua>>
}

impl <'lua> ToLua<'lua> for Property<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        let table = lua.create_table();
        table.set("name", self.name)?;
        let metatable = lua.create_table();
        metatable.set("__call", self.cb_new
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        metatable.set("__index", self.cb_index
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        metatable.set("__newindex", self.cb_newindex
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        table.set_metatable(Some(metatable));
        Ok(Value::Table(table))
    }
}

impl <'lua> FromLua<'lua> for Property<'lua> {
    fn from_lua(val: Value<'lua>, _: &'lua Lua) -> rlua::Result<Self> {
        if let Value::Table(table) = val {
            let name = table.get("name")?;
            let meta = table.get_metatable()
                .expect("Property table had no metatable");
            let cb_new = meta.get("__call").ok();
            let cb_index = meta.get("__index").ok();
            let cb_newindex = meta.get("__newindex").ok();
            Ok(Property {
                name,
                cb_new,
                cb_index,
                cb_newindex
            })
        } else {
            use rlua::Error::FromLuaConversionError;
            Err(FromLuaConversionError {
                from: "something else",
                to: "Property",
                message: None
            })
        }
    }
}

impl <'lua> Property <'lua> {
    pub fn new(name: String,
               cb_new: Option<PropF<'lua>>,
               cb_index: Option<PropF<'lua>>,
               cb_newindex: Option<PropF<'lua>>) -> Self {
        Property { name, cb_new, cb_index, cb_newindex }
    }
}
<commit_msg>Added clarifying documentation to Property<commit_after>use rlua::{self, Lua, Value, ToLua, FromLua};

pub type PropF<'lua> = rlua::Function<'lua>;

/// Type that represents a Lua table with these fields.
///
/// NOTE Not actually UserData, because it has lua functions in it.
/// It's just the deserialized version of a table.
pub struct Property<'lua> {
    pub name: String,
    pub cb_new: Option<PropF<'lua>>,
    pub cb_index: Option<PropF<'lua>>,
    pub cb_newindex: Option<PropF<'lua>>
}

impl <'lua> ToLua<'lua> for Property<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        let table = lua.create_table();
        table.set("name", self.name)?;
        let metatable = lua.create_table();
        metatable.set("__call", self.cb_new
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        metatable.set("__index", self.cb_index
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        metatable.set("__newindex", self.cb_newindex
                  .map(Value::Function)
                  .unwrap_or(Value::Nil))?;
        table.set_metatable(Some(metatable));
        Ok(Value::Table(table))
    }
}

impl <'lua> FromLua<'lua> for Property<'lua> {
    fn from_lua(val: Value<'lua>, _: &'lua Lua) -> rlua::Result<Self> {
        if let Value::Table(table) = val {
            let name = table.get("name")?;
            let meta = table.get_metatable()
                .expect("Property table had no metatable");
            let cb_new = meta.get("__call").ok();
            let cb_index = meta.get("__index").ok();
            let cb_newindex = meta.get("__newindex").ok();
            Ok(Property {
                name,
                cb_new,
                cb_index,
                cb_newindex
            })
        } else {
            use rlua::Error::FromLuaConversionError;
            Err(FromLuaConversionError {
                from: "something else",
                to: "Property",
                message: None
            })
        }
    }
}

impl <'lua> Property <'lua> {
    pub fn new(name: String,
               cb_new: Option<PropF<'lua>>,
               cb_index: Option<PropF<'lua>>,
               cb_newindex: Option<PropF<'lua>>) -> Self {
        Property { name, cb_new, cb_index, cb_newindex }
    }
}
<|endoftext|>


<commit_before>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let mut database = bot.database.lock().unwrap();
    let user = database.autotags.users.iter()
      .find(|u| u.user_id == who.0);
    let is_verified = user
      .map(|u| u.verification.verified)
      .unwrap_or_default();

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    if let Some(u) = user {
      if is_verified && !ignore_verified && char_id != u.character_id {
        return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
      }
    }

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    database.autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<commit_msg>fix: make it work<commit_after>pub mod tag;
pub mod autotag;
pub mod update_tags;

pub use self::tag::*;
pub use self::autotag::*;
pub use self::update_tags::*;

use LalafellBot;
use database::AutotagUser;

use xivdb::error::*;

use discord::model::{UserId, LiveServer, Role, RoleId};

use std::sync::Arc;
use std::collections::HashMap;

pub struct Tagger;

impl Tagger {
  pub fn search_tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, server: &str, character_name: &str, ignore_verified: bool) -> Result<Option<String>> {
    let mut params = HashMap::new();
    params.insert(String::from("one"), String::from("characters"));
    params.insert(String::from("strict"), String::from("on"));
    params.insert(String::from("server|et"), server.to_string());

    let res = bot.xivdb.search(character_name.to_string(), params).chain_err(|| "could not query XIVDB")?;

    let search_chars = res.characters.unwrap().results;
    if search_chars.is_empty() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    let char_id = match search_chars[0]["id"].as_u64() {
      Some(u) => u,
      None => return Err("character ID was not a u64".into())
    };

    let name = match search_chars[0]["name"].as_str() {
      Some(s) => s,
      None => return Err("character name was not a string".into())
    };

    if name.to_lowercase() != character_name.to_lowercase() {
      return Ok(Some(format!("Could not find any character by the name {} on {}.", character_name, server)));
    }

    Tagger::tag(bot, who, on, char_id, ignore_verified)
  }

  pub fn tag(bot: Arc<LalafellBot>, who: UserId, on: &LiveServer, char_id: u64, ignore_verified: bool) -> Result<Option<String>> {
    let is_verified = match bot.database.lock().unwrap().autotags.users.iter().find(|u| u.user_id == who.0 && u.server_id == on.id.0) {
      Some(u) => {
        if u.verification.verified && !ignore_verified && char_id != u.character_id {
          return Ok(Some(format!("{} is verified as {} on {}, so they cannot switch to another account.", who.mention(), u.character, u.server)));
        }
        u.verification.verified
      },
      None => false
    };

    let member = bot.discord.get_member(on.id, who).chain_err(|| "could not get member for tagging")?;

    let character = bot.xivdb.character(char_id).chain_err(|| "could not look up character")?;

    bot.database.lock().unwrap().autotags.update_or_remove(AutotagUser::new(
      who.0,
      on.id.0,
      character.lodestone_id,
      &character.name,
      &character.server
    ));

    let roles = &on.roles;
    let mut add_roles = Vec::new();
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.race.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.data.gender.to_lowercase()) {
      add_roles.push(r);
    }
    if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == character.server.to_lowercase()) {
      add_roles.push(r);
    }
    if is_verified {
      if let Some(r) = roles.iter().find(|x| x.name.to_lowercase() == "verified") {
        add_roles.push(r);
      }
    }

    let all_group_roles: Vec<&String> = bot.config.roles.groups.iter().flat_map(|x| x).collect();
    let keep: Vec<&Role> = roles.iter().filter(|x| member.roles.contains(&x.id)).collect();
    let keep: Vec<&Role> = keep.into_iter().filter(|x| !all_group_roles.contains(&&x.name)).collect();
    let mut role_set: Vec<RoleId> = add_roles.iter().map(|r| r.id).chain(keep.into_iter().map(|r| r.id)).collect();
    role_set.sort();
    role_set.dedup();

    if !role_set.iter().all(|r| member.roles.contains(r)) {
      bot.discord.edit_member_roles(on.id, who, &role_set).chain_err(|| "could not add roles")?;
    }

    // cannot edit nickname of those with a higher role
    match member.nick {
      Some(ref nick) if *nick != character.name => {
        bot.discord.edit_member(on.id, who, |e| e.nickname(&character.name)).ok();
      },
      _ => {}
    }
    Ok(None)
  }
}
<|endoftext|>


<commit_before>//! Client for connecting and communicating with a server listener which speaks SrvProtocol.
//!
//! # RPC Call Example
//!
//! ```rust no_run
//! use habitat_common::types::ListenCtlAddr;
//! use habitat_sup_client::SrvClient;
//! use habitat_sup_protocol as protocols;
//! use futures::stream::StreamExt;
//!
//! #[tokio::main]
//! async fn main() {
//!     let listen_addr = ListenCtlAddr::default();
//!     let secret_key = "seekrit";
//!     let msg = protocols::ctl::SvcGetDefaultCfg::default();
//!     let mut response = SrvClient::request(&listen_addr, secret_key, msg).await.unwrap();
//!     while let Some(message_result) = response.next().await {
//!         let reply = message_result.unwrap();
//!         match reply.message_id() {
//!             "ServiceCfg" => {
//!                 let m = reply.parse::<protocols::types::ServiceCfg>().unwrap();
//!                 println!("{}", m.default.unwrap_or_default());
//!             }
//!             "NetErr" => {
//!                 let m = reply.parse::<protocols::net::NetErr>().unwrap();
//!                 println!("{}", m);
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use habitat_sup_protocol as protocol;
#[macro_use]
extern crate log;
use crate::{common::types::ListenCtlAddr,
            protocol::{codec::*,
                       net::NetErr}};
use futures::{sink::SinkExt,
              stream::{Stream,
                       StreamExt}};
use habitat_common as common;
use std::{error,
          fmt,
          io,
          path::PathBuf};
use tokio::net::TcpStream;
use tokio_util::codec::Framed;

/// Error types returned by a [`SrvClient`].
#[derive(Debug)]
pub enum SrvClientError {
    /// Connection refused
    ConnectionRefused,
    /// The remote server unexpectedly closed the connection.
    ConnectionClosed,
    /// Unable to locate a secret key on disk.
    CtlSecretNotFound(PathBuf),
    /// Decoding a message from the remote failed.
    Decode(prost::DecodeError),
    /// An Os level IO error occurred.
    Io(io::Error),
    /// An RPC call to the remote was received but failed.
    NetErr(NetErr),
    /// A parse error from an Invalid Color string
    ParseColor(termcolor::ParseColorError),
}

impl error::Error for SrvClientError {}

impl fmt::Display for SrvClientError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let content = match *self {
            SrvClientError::ConnectionClosed => {
                "The connection was unexpectedly closed.\n\nThis may be because the given \
                 Supervisor is in the middle of an orderly shutdown,\nand is no longer processing \
                 command requests."
                                   .to_string()
            }
            SrvClientError::ConnectionRefused => {
                "Unable to contact the Supervisor.\n\nIf the Supervisor you are contacting is \
                 local, this probably means it is not running. You can run a Supervisor in the \
                 foreground with:\n\nhab sup run\n\nOr try restarting the Supervisor through your \
                 operating system's init process or Windows service."
                                                                     .to_string()
            }
            SrvClientError::CtlSecretNotFound(ref path) => {
                format!("No Supervisor CtlGateway secret set in `cli.toml` or found at {}. Run \
                         `hab setup` or run the Supervisor for the first time before attempting \
                         to command the Supervisor.",
                        path.display())
            }
            SrvClientError::Decode(ref err) => format!("{}", err),
            SrvClientError::Io(ref err) => format!("{}", err),
            SrvClientError::NetErr(ref err) => format!("{}", err),
            SrvClientError::ParseColor(ref err) => format!("{}", err),
        };
        write!(f, "{}", content)
    }
}

impl From<NetErr> for SrvClientError {
    fn from(err: NetErr) -> Self { SrvClientError::NetErr(err) }
}

impl From<io::Error> for SrvClientError {
    fn from(err: io::Error) -> Self {
        match err.kind() {
            io::ErrorKind::ConnectionRefused => SrvClientError::ConnectionRefused,
            _ => SrvClientError::Io(err),
        }
    }
}

impl From<prost::DecodeError> for SrvClientError {
    fn from(err: prost::DecodeError) -> Self { SrvClientError::Decode(err) }
}

impl From<termcolor::ParseColorError> for SrvClientError {
    fn from(err: termcolor::ParseColorError) -> Self { SrvClientError::ParseColor(err) }
}

/// Client for connecting and communicating with a server speaking SrvProtocol.
///
/// See module doc for usage.
pub struct SrvClient;

impl SrvClient {
    /// Connect to the remote server with the given secret_key and make a request.
    ///
    /// Returns a stream of `SrvMessage`'s representing the server response.
    pub async fn request(
        address: &ListenCtlAddr,
        secret_key: &str,
        request: impl Into<SrvMessage> + fmt::Debug)
        -> Result<impl Stream<Item = Result<SrvMessage, io::Error>>, SrvClientError> {
        println!("IN NEW request");

        let socket = TcpStream::connect(address.as_ref()).await?;
        let mut socket = Framed::new(socket, SrvCodec::new());
        let mut current_transaction = SrvTxn::default();

        // Send the handshake message to the server
        let mut handshake = protocol::ctl::Handshake::default();
        handshake.secret_key = Some(String::from(secret_key));
        let mut message = SrvMessage::from(handshake);
        message.set_transaction(current_transaction);
        socket.send(message).await?;

        // Verify the handshake response
        socket.next()
              .await
              .ok_or(SrvClientError::ConnectionClosed)??;

        // Send the actual request message
        current_transaction.increment();
        let mut message = request.into();
        message.set_transaction(current_transaction);
        trace!("Sending SrvMessage -> {:?}", message);
        socket.send(message).await?;

        // Return the socket for use as a Stream of responses
        Ok(socket)
    }

    pub fn read_secret_key() -> Result<String, SrvClientError> {
        let mut buf = String::new();
        protocol::read_secret_key(protocol::sup_root(None), &mut buf)
            .map_err(SrvClientError::from)?;
        Ok(buf)
    }
}
<commit_msg>Remove debug print statement<commit_after>//! Client for connecting and communicating with a server listener which speaks SrvProtocol.
//!
//! # RPC Call Example
//!
//! ```rust no_run
//! use habitat_common::types::ListenCtlAddr;
//! use habitat_sup_client::SrvClient;
//! use habitat_sup_protocol as protocols;
//! use futures::stream::StreamExt;
//!
//! #[tokio::main]
//! async fn main() {
//!     let listen_addr = ListenCtlAddr::default();
//!     let secret_key = "seekrit";
//!     let msg = protocols::ctl::SvcGetDefaultCfg::default();
//!     let mut response = SrvClient::request(&listen_addr, secret_key, msg).await.unwrap();
//!     while let Some(message_result) = response.next().await {
//!         let reply = message_result.unwrap();
//!         match reply.message_id() {
//!             "ServiceCfg" => {
//!                 let m = reply.parse::<protocols::types::ServiceCfg>().unwrap();
//!                 println!("{}", m.default.unwrap_or_default());
//!             }
//!             "NetErr" => {
//!                 let m = reply.parse::<protocols::net::NetErr>().unwrap();
//!                 println!("{}", m);
//!             }
//!             _ => (),
//!         }
//!     }
//! }
//! ```

use habitat_sup_protocol as protocol;
#[macro_use]
extern crate log;
use crate::{common::types::ListenCtlAddr,
            protocol::{codec::*,
                       net::NetErr}};
use futures::{sink::SinkExt,
              stream::{Stream,
                       StreamExt}};
use habitat_common as common;
use std::{error,
          fmt,
          io,
          path::PathBuf};
use tokio::net::TcpStream;
use tokio_util::codec::Framed;

/// Error types returned by a [`SrvClient`].
#[derive(Debug)]
pub enum SrvClientError {
    /// Connection refused
    ConnectionRefused,
    /// The remote server unexpectedly closed the connection.
    ConnectionClosed,
    /// Unable to locate a secret key on disk.
    CtlSecretNotFound(PathBuf),
    /// Decoding a message from the remote failed.
    Decode(prost::DecodeError),
    /// An Os level IO error occurred.
    Io(io::Error),
    /// An RPC call to the remote was received but failed.
    NetErr(NetErr),
    /// A parse error from an Invalid Color string
    ParseColor(termcolor::ParseColorError),
}

impl error::Error for SrvClientError {}

impl fmt::Display for SrvClientError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let content = match *self {
            SrvClientError::ConnectionClosed => {
                "The connection was unexpectedly closed.\n\nThis may be because the given \
                 Supervisor is in the middle of an orderly shutdown,\nand is no longer processing \
                 command requests."
                                   .to_string()
            }
            SrvClientError::ConnectionRefused => {
                "Unable to contact the Supervisor.\n\nIf the Supervisor you are contacting is \
                 local, this probably means it is not running. You can run a Supervisor in the \
                 foreground with:\n\nhab sup run\n\nOr try restarting the Supervisor through your \
                 operating system's init process or Windows service."
                                                                     .to_string()
            }
            SrvClientError::CtlSecretNotFound(ref path) => {
                format!("No Supervisor CtlGateway secret set in `cli.toml` or found at {}. Run \
                         `hab setup` or run the Supervisor for the first time before attempting \
                         to command the Supervisor.",
                        path.display())
            }
            SrvClientError::Decode(ref err) => format!("{}", err),
            SrvClientError::Io(ref err) => format!("{}", err),
            SrvClientError::NetErr(ref err) => format!("{}", err),
            SrvClientError::ParseColor(ref err) => format!("{}", err),
        };
        write!(f, "{}", content)
    }
}

impl From<NetErr> for SrvClientError {
    fn from(err: NetErr) -> Self { SrvClientError::NetErr(err) }
}

impl From<io::Error> for SrvClientError {
    fn from(err: io::Error) -> Self {
        match err.kind() {
            io::ErrorKind::ConnectionRefused => SrvClientError::ConnectionRefused,
            _ => SrvClientError::Io(err),
        }
    }
}

impl From<prost::DecodeError> for SrvClientError {
    fn from(err: prost::DecodeError) -> Self { SrvClientError::Decode(err) }
}

impl From<termcolor::ParseColorError> for SrvClientError {
    fn from(err: termcolor::ParseColorError) -> Self { SrvClientError::ParseColor(err) }
}

/// Client for connecting and communicating with a server speaking SrvProtocol.
///
/// See module doc for usage.
pub struct SrvClient;

impl SrvClient {
    /// Connect to the remote server with the given secret_key and make a request.
    ///
    /// Returns a stream of `SrvMessage`'s representing the server response.
    pub async fn request(
        address: &ListenCtlAddr,
        secret_key: &str,
        request: impl Into<SrvMessage> + fmt::Debug)
        -> Result<impl Stream<Item = Result<SrvMessage, io::Error>>, SrvClientError> {
        let socket = TcpStream::connect(address.as_ref()).await?;
        let mut socket = Framed::new(socket, SrvCodec::new());
        let mut current_transaction = SrvTxn::default();

        // Send the handshake message to the server
        let mut handshake = protocol::ctl::Handshake::default();
        handshake.secret_key = Some(String::from(secret_key));
        let mut message = SrvMessage::from(handshake);
        message.set_transaction(current_transaction);
        socket.send(message).await?;

        // Verify the handshake response
        socket.next()
              .await
              .ok_or(SrvClientError::ConnectionClosed)??;

        // Send the actual request message
        current_transaction.increment();
        let mut message = request.into();
        message.set_transaction(current_transaction);
        trace!("Sending SrvMessage -> {:?}", message);
        socket.send(message).await?;

        // Return the socket for use as a Stream of responses
        Ok(socket)
    }

    pub fn read_secret_key() -> Result<String, SrvClientError> {
        let mut buf = String::new();
        protocol::read_secret_key(protocol::sup_root(None), &mut buf)
            .map_err(SrvClientError::from)?;
        Ok(buf)
    }
}
<|endoftext|>


<commit_before>//! Implementation of input backend trait for types provided by `libinput`

use backend::SeatInternal;
use backend::input::{InputBackend, InputHandler, Seat, SeatCapabilities};
use input::{Libinput, Device, Seat as LibinputSeat, DeviceCapability};
use input::event::*;

use std::io::Error as IoError;
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
use std::hash::{Hash, Hasher};

pub struct LibinputInputBackend {
    context: Libinput,
    devices: Vec<Device>,
    seats: HashMap<LibinputSeat, Seat>,
    handler: Option<Box<InputHandler<LibinputInputBackend> + 'static>>,
}

impl InputBackend for LibinputInputBackend {
    type InputConfig = [Device];
    type EventError = IoError;

    fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
        if self.handler.is_some() {
            self.clear_handler();
        }
        for seat in self.seats.values() {
            handler.on_seat_created(&seat);
        }
        self.handler = Some(Box::new(handler));
    }

    fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
        self.handler
            .as_mut()
            .map(|handler| handler as &mut InputHandler<Self>)
    }

    fn clear_handler(&mut self) {
        if let Some(mut handler) = self.handler.take() {
            for seat in self.seats.values() {
                handler.on_seat_destroyed(&seat);
            }
        }
    }

    fn input_config(&mut self) -> &mut Self::InputConfig {
        &mut self.devices
    }

    fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> {
        // FIXME later.
        // This will be doable with the hardware cursor api and probably some more cases
        Err(())
    }

    fn dispatch_new_events(&mut self) -> Result<(), IoError> {
        self.context.dispatch()?;
        for event in &mut self.context {
            match event {
                Event::Device(device_event) => {
                    use input::event::device::*;
                    match device_event {
                        DeviceEvent::Added(device_added_event) => {
                            let added = device_added_event.into_event().device();

                            let new_caps = SeatCapabilities {
                                pointer: added.has_capability(DeviceCapability::Pointer),
                                keyboard: added.has_capability(DeviceCapability::Keyboard),
                                touch: added.has_capability(DeviceCapability::Touch),
                            };

                            let device_seat = added.seat();
                            self.devices.push(added);

                            match self.seats.entry(device_seat) {
                                Entry::Occupied(mut seat_entry) => {
                                    let old_seat = seat_entry.get_mut();
                                    {
                                        let caps = old_seat.capabilities_mut();
                                        caps.pointer = new_caps.pointer || caps.pointer;
                                        caps.keyboard = new_caps.keyboard || caps.keyboard;
                                        caps.touch = new_caps.touch || caps.touch;
                                    }
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_changed(old_seat);
                                    }
                                },
                                Entry::Vacant(seat_entry) => {
                                    let mut hasher = DefaultHasher::default();
                                    seat_entry.key().hash(&mut hasher);
                                    let seat = seat_entry.insert(Seat::new(hasher.finish(), new_caps));
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_created(seat);
                                    }
                                }
                            }
                        },
                        DeviceEvent::Removed(device_removed_event) => {
                            let removed = device_removed_event.into_event().device();

                            // remove device
                            self.devices.retain(|dev| *dev == removed);

                            let device_seat = removed.seat();

                            // update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
                            if let Some(seat) = self.seats.get_mut(&device_seat) {
                                let caps = seat.capabilities_mut();
                                caps.pointer = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Pointer));
                                caps.keyboard = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Keyboard));
                                caps.touch = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Touch));
                            } else {
                                panic!("Seat changed that was never created")
                            }

                            // check if the seat has any other devices
                            if !self.devices.iter().any(|x| x.seat() == device_seat) {
                                // it has not, lets destroy it
                                if let Some(seat) = self.seats.remove(&device_seat) {
                                    if let Some(ref mut handler) = self.handler {
                                        handler.on_seat_destroyed(&seat);
                                    }
                                } else {
                                    panic!("Seat destroyed that was never created");
                                }
                            } else {
                                // it has, notify about updates
                                if let Some(ref mut handler) = self.handler {
                                    handler.on_seat_changed(self.seats.get(&device_seat).unwrap());
                                }
                            }
                        },
                    }
                    if let Some(ref mut handler) = self.handler {
                       handler.on_input_config_changed(&mut self.devices);
                    }
                },
                Event::Touch(touch_event) => {},
                Event::Keyboard(keyboard_event) => {},
                Event::Pointer(pointer_event) => {},
                _ => {}, //FIXME: What to do with the rest.
            }
        };
        Ok(())
    }
}
<commit_msg>Add logging to libinput backend<commit_after>//! Implementation of input backend trait for types provided by `libinput`

use backend::SeatInternal;
use backend::input::{InputBackend, InputHandler, Seat, SeatCapabilities};
use input::{Libinput, Device, Seat as LibinputSeat, DeviceCapability};
use input::event::*;

use std::io::Error as IoError;
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
use std::hash::{Hash, Hasher};

pub struct LibinputInputBackend {
    context: Libinput,
    devices: Vec<Device>,
    seats: HashMap<LibinputSeat, Seat>,
    handler: Option<Box<InputHandler<LibinputInputBackend> + 'static>>,
    logger: ::slog::Logger,
}

impl InputBackend for LibinputInputBackend {
    type InputConfig = [Device];
    type EventError = IoError;

    fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
        if self.handler.is_some() {
            self.clear_handler();
        }
        info!(self.logger, "New input handler set.");
        for seat in self.seats.values() {
            trace!(self.logger, "Calling on_seat_created with {:?}", seat);
            handler.on_seat_created(&seat);
        }
        self.handler = Some(Box::new(handler));
    }

    fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
        self.handler
            .as_mut()
            .map(|handler| handler as &mut InputHandler<Self>)
    }

    fn clear_handler(&mut self) {
        if let Some(mut handler) = self.handler.take() {
            for seat in self.seats.values() {
                trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
                handler.on_seat_destroyed(&seat);
            }
            info!(self.logger, "Removing input handler");
        }
    }

    fn input_config(&mut self) -> &mut Self::InputConfig {
        &mut self.devices
    }

    fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> {
        // FIXME later.
        // This will be doable with the hardware cursor api and probably some more cases
        warn!(self.logger, "Setting the cursor position is currently unsupported by the libinput backend");
        Err(())
    }

    fn dispatch_new_events(&mut self) -> Result<(), IoError> {
        self.context.dispatch()?;
        for event in &mut self.context {
            match event {
                Event::Device(device_event) => {
                    use input::event::device::*;
                    match device_event {
                        DeviceEvent::Added(device_added_event) => {
                            let added = device_added_event.into_event().device();

                            let new_caps = SeatCapabilities {
                                pointer: added.has_capability(DeviceCapability::Pointer),
                                keyboard: added.has_capability(DeviceCapability::Keyboard),
                                touch: added.has_capability(DeviceCapability::Touch),
                            };

                            let device_seat = added.seat();
                            self.devices.push(added);

                            match self.seats.entry(device_seat) {
                                Entry::Occupied(mut seat_entry) => {
                                    let old_seat = seat_entry.get_mut();
                                    {
                                        let caps = old_seat.capabilities_mut();
                                        caps.pointer = new_caps.pointer || caps.pointer;
                                        caps.keyboard = new_caps.keyboard || caps.keyboard;
                                        caps.touch = new_caps.touch || caps.touch;
                                    }
                                    if let Some(ref mut handler) = self.handler {
                                        trace!(self.logger, "Calling on_seat_changed with {:?}", old_seat);
                                        handler.on_seat_changed(old_seat);
                                    }
                                },
                                Entry::Vacant(seat_entry) => {
                                    let mut hasher = DefaultHasher::default();
                                    seat_entry.key().hash(&mut hasher);
                                    let seat = seat_entry.insert(Seat::new(hasher.finish(), new_caps));
                                    if let Some(ref mut handler) = self.handler {
                                        trace!(self.logger, "Calling on_seat_created with {:?}", seat);
                                        handler.on_seat_created(seat);
                                    }
                                }
                            }
                        },
                        DeviceEvent::Removed(device_removed_event) => {
                            let removed = device_removed_event.into_event().device();

                            // remove device
                            self.devices.retain(|dev| *dev == removed);

                            let device_seat = removed.seat();

                            // update capabilities, so they appear correctly on `on_seat_changed` and `on_seat_destroyed`.
                            if let Some(seat) = self.seats.get_mut(&device_seat) {
                                let caps = seat.capabilities_mut();
                                caps.pointer = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Pointer));
                                caps.keyboard = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Keyboard));
                                caps.touch = self.devices.iter().filter(|x| x.seat() == device_seat).any(|x| x.has_capability(DeviceCapability::Touch));
                            } else {
                                panic!("Seat changed that was never created")
                            }

                            // check if the seat has any other devices
                            if !self.devices.iter().any(|x| x.seat() == device_seat) {
                                // it has not, lets destroy it
                                if let Some(seat) = self.seats.remove(&device_seat) {
                                    if let Some(ref mut handler) = self.handler {
                                        trace!(self.logger, "Calling on_seat_destroyed with {:?}", seat);
                                        handler.on_seat_destroyed(&seat);
                                    }
                                } else {
                                    panic!("Seat destroyed that was never created");
                                }
                            } else {
                                // it has, notify about updates
                                if let Some(ref mut handler) = self.handler {
                                    let seat = self.seats.get(&device_seat).unwrap();
                                    trace!(self.logger, "Calling on_seat_changed with {:?}", seat);
                                    handler.on_seat_changed(seat);
                                }
                            }
                        },
                    }
                    if let Some(ref mut handler) = self.handler {
                       handler.on_input_config_changed(&mut self.devices);
                    }
                },
                Event::Touch(touch_event) => {},
                Event::Keyboard(keyboard_event) => {},
                Event::Pointer(pointer_event) => {},
                _ => {}, //FIXME: What to do with the rest.
            }
        };
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::{
    config, db,
    models::Version,
    schema::{crates, readme_renderings, versions},
    uploaders::Uploader,
};
use anyhow::{anyhow, Context};
use std::{io::Read, path::Path, sync::Arc, thread};

use cargo_registry_markdown::text_to_html;
use chrono::{TimeZone, Utc};
use diesel::{dsl::any, prelude::*};
use flate2::read::GzDecoder;
use reqwest::{blocking::Client, header};
use tar::{self, Archive};

const CACHE_CONTROL_README: &str = "public,max-age=604800";
const USER_AGENT: &str = "crates-admin";

#[derive(clap::Parser, Debug)]
#[clap(
    name = "render-readmes",
    about = "Iterates over every crate versions ever uploaded and (re-)renders their \
        readme using the readme renderer from the cargo_registry crate.",
    after_help = "Warning: this can take a lot of time."
)]
pub struct Opts {
    /// How many versions should be queried and processed at a time.
    #[clap(long, default_value = "25")]
    page_size: usize,

    /// Only rerender readmes that are older than this date.
    #[clap(long)]
    older_than: Option<String>,

    /// Only rerender readmes for the specified crate.
    #[clap(long = "crate")]
    crate_name: Option<String>,
}

pub fn run(opts: Opts) {
    let base_config = Arc::new(config::Base::from_environment());
    let conn = db::connect_now().unwrap();

    let start_time = Utc::now();

    let older_than = if let Some(ref time) = opts.older_than {
        Utc.datetime_from_str(time, "%Y-%m-%d %H:%M:%S")
            .expect("Could not parse --older-than argument as a time")
    } else {
        start_time
    };
    let older_than = older_than.naive_utc();

    println!("Start time:                   {}", start_time);
    println!("Rendering readmes older than: {}", older_than);

    let mut query = versions::table
        .inner_join(crates::table)
        .left_outer_join(readme_renderings::table)
        .filter(
            readme_renderings::rendered_at
                .lt(older_than)
                .or(readme_renderings::version_id.is_null()),
        )
        .select(versions::id)
        .into_boxed();

    if let Some(crate_name) = opts.crate_name {
        println!("Rendering readmes for {}", crate_name);
        query = query.filter(crates::name.eq(crate_name));
    }

    let version_ids: Vec<i32> = query.load(&conn).expect("error loading version ids");

    let total_versions = version_ids.len();
    println!("Rendering {} versions", total_versions);

    let page_size = opts.page_size;

    let total_pages = total_versions / page_size;
    let total_pages = if total_versions % page_size == 0 {
        total_pages
    } else {
        total_pages + 1
    };

    let client = Client::new();

    for (page_num, version_ids_chunk) in version_ids.chunks(page_size).enumerate() {
        println!(
            "= Page {} of {} ==================================",
            page_num + 1,
            total_pages
        );

        let versions: Vec<(Version, String)> = versions::table
            .inner_join(crates::table)
            .filter(versions::id.eq(any(version_ids_chunk)))
            .select((versions::all_columns, crates::name))
            .load(&conn)
            .expect("error loading versions");

        let mut tasks = Vec::with_capacity(page_size as usize);
        for (version, krate_name) in versions {
            Version::record_readme_rendering(version.id, &conn).unwrap_or_else(|_| {
                panic!(
                    "[{}-{}] Couldn't record rendering time",
                    krate_name, version.num
                )
            });
            let client = client.clone();
            let base_config = base_config.clone();
            let handle = thread::spawn(move || {
                println!("[{}-{}] Rendering README...", krate_name, version.num);
                let readme = get_readme(base_config.uploader(), &client, &version, &krate_name);
                if readme.is_err() {
                    return;
                }
                let readme = readme.unwrap();
                let content_length = readme.len() as u64;
                let content = std::io::Cursor::new(readme);
                let readme_path = format!("readmes/{0}/{0}-{1}.html", krate_name, version.num);
                let mut extra_headers = header::HeaderMap::new();
                extra_headers.insert(
                    header::CACHE_CONTROL,
                    header::HeaderValue::from_static(CACHE_CONTROL_README),
                );
                base_config
                    .uploader()
                    .upload(
                        &client,
                        &readme_path,
                        content,
                        content_length,
                        "text/html",
                        extra_headers,
                    )
                    .unwrap_or_else(|_| {
                        panic!(
                            "[{}-{}] Couldn't upload file to S3",
                            krate_name, version.num
                        )
                    });
            });
            tasks.push(handle);
        }
        for handle in tasks {
            if let Err(err) = handle.join() {
                println!("Thread panicked: {:?}", err);
            }
        }
    }
}

/// Renders the readme of an uploaded crate version.
fn get_readme(
    uploader: &Uploader,
    client: &Client,
    version: &Version,
    krate_name: &str,
) -> anyhow::Result<String> {
    let pkg_name = format!("{}-{}", krate_name, version.num);

    let location = uploader.crate_location(krate_name, &version.num.to_string());

    let location = match uploader {
        Uploader::S3 { .. } => location,
        Uploader::Local => format!("http://localhost:8888/{}", location),
    };

    let mut extra_headers = header::HeaderMap::new();
    extra_headers.insert(
        header::USER_AGENT,
        header::HeaderValue::from_static(USER_AGENT),
    );
    let request = client.get(&location).headers(extra_headers);
    let response = request.send().context("Failed to fetch crate")?;

    if !response.status().is_success() {
        return Err(anyhow!(
            "Failed to get a 200 response: {}",
            response.text().unwrap()
        ));
    }

    let reader = GzDecoder::new(response);
    let archive = Archive::new(reader);
    render_pkg_readme(archive, &pkg_name)
}

fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow::Result<String> {
    let mut entries = archive.entries().context("Invalid tar archive entries")?;

    let manifest: Manifest = {
        let path = format!("{}/Cargo.toml", pkg_name);
        let contents = find_file_by_path(&mut entries, Path::new(&path))
            .context("Failed to read Cargo.toml file")?;

        toml::from_str(&contents).context("Failed to parse manifest file")?
    };

    let rendered = {
        let readme_path = manifest
            .package
            .readme
            .clone()
            .unwrap_or_else(|| "README.md".into());
        let path = format!("{}/{}", pkg_name, readme_path);
        let contents = find_file_by_path(&mut entries, Path::new(&path))
            .with_context(|| format!("Failed to read {} file", readme_path))?;

        text_to_html(
            &contents,
            &readme_path,
            manifest.package.repository.as_deref(),
        )
    };
    return Ok(rendered);

    #[derive(Debug, Deserialize)]
    struct Package {
        readme: Option<String>,
        repository: Option<String>,
    }

    #[derive(Debug, Deserialize)]
    struct Manifest {
        package: Package,
    }
}

/// Search an entry by its path in a Tar archive.
fn find_file_by_path<R: Read>(
    entries: &mut tar::Entries<'_, R>,
    path: &Path,
) -> anyhow::Result<String> {
    let mut file = entries
        .filter_map(|entry| entry.ok())
        .find(|file| match file.path() {
            Ok(p) => p == path,
            Err(_) => false,
        })
        .ok_or_else(|| anyhow!("Failed to find tarball entry: {}", path.display()))?;

    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .context("Failed to read file contents")?;

    Ok(contents)
}

#[cfg(test)]
pub mod tests {
    use std::io::Write;
    use tar;

    use super::render_pkg_readme;

    pub fn add_file<W: Write>(pkg: &mut tar::Builder<W>, path: &str, content: &[u8]) {
        let mut header = tar::Header::new_gnu();
        header.set_size(content.len() as u64);
        header.set_cksum();
        pkg.append_data(&mut header, path, content).unwrap();
    }

    #[test]
    fn test_render_pkg_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "README.md"
"#,
        );
        add_file(&mut pkg, "foo-0.0.1/README.md", b"readme");
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("readme"))
    }

    #[test]
    fn test_render_pkg_no_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
"#,
        );
        let serialized_archive = pkg.into_inner().unwrap();
        assert_err!(render_pkg_readme(
            tar::Archive::new(&*serialized_archive),
            "foo-0.0.1"
        ));
    }

    #[test]
    fn test_render_pkg_implicit_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
"#,
        );
        add_file(&mut pkg, "foo-0.0.1/README.md", b"readme");
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("readme"))
    }

    #[test]
    fn test_render_pkg_readme_w_link() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "README.md"
repository = "https://github.com/foo/foo"
"#,
        );
        add_file(
            &mut pkg,
            "foo-0.0.1/README.md",
            b"readme [link](./Other.md)",
        );
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/./Other.md\""))
    }

    #[test]
    fn test_render_pkg_readme_not_at_root() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "docs/README.md"
repository = "https://github.com/foo/foo"
"#,
        );
        add_file(
            &mut pkg,
            "foo-0.0.1/docs/README.md",
            b"docs/readme [link](./Other.md)",
        );
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("docs/readme"));
        assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/docs/./Other.md\""))
    }
}
<commit_msg>admin::render_readmes: Return `Result` from `thread::spawn()` closure<commit_after>use crate::{
    config, db,
    models::Version,
    schema::{crates, readme_renderings, versions},
    uploaders::Uploader,
};
use anyhow::{anyhow, Context};
use std::{io::Read, path::Path, sync::Arc, thread};

use cargo_registry_markdown::text_to_html;
use chrono::{TimeZone, Utc};
use diesel::{dsl::any, prelude::*};
use flate2::read::GzDecoder;
use reqwest::{blocking::Client, header};
use tar::{self, Archive};

const CACHE_CONTROL_README: &str = "public,max-age=604800";
const USER_AGENT: &str = "crates-admin";

#[derive(clap::Parser, Debug)]
#[clap(
    name = "render-readmes",
    about = "Iterates over every crate versions ever uploaded and (re-)renders their \
        readme using the readme renderer from the cargo_registry crate.",
    after_help = "Warning: this can take a lot of time."
)]
pub struct Opts {
    /// How many versions should be queried and processed at a time.
    #[clap(long, default_value = "25")]
    page_size: usize,

    /// Only rerender readmes that are older than this date.
    #[clap(long)]
    older_than: Option<String>,

    /// Only rerender readmes for the specified crate.
    #[clap(long = "crate")]
    crate_name: Option<String>,
}

pub fn run(opts: Opts) {
    let base_config = Arc::new(config::Base::from_environment());
    let conn = db::connect_now().unwrap();

    let start_time = Utc::now();

    let older_than = if let Some(ref time) = opts.older_than {
        Utc.datetime_from_str(time, "%Y-%m-%d %H:%M:%S")
            .expect("Could not parse --older-than argument as a time")
    } else {
        start_time
    };
    let older_than = older_than.naive_utc();

    println!("Start time:                   {}", start_time);
    println!("Rendering readmes older than: {}", older_than);

    let mut query = versions::table
        .inner_join(crates::table)
        .left_outer_join(readme_renderings::table)
        .filter(
            readme_renderings::rendered_at
                .lt(older_than)
                .or(readme_renderings::version_id.is_null()),
        )
        .select(versions::id)
        .into_boxed();

    if let Some(crate_name) = opts.crate_name {
        println!("Rendering readmes for {}", crate_name);
        query = query.filter(crates::name.eq(crate_name));
    }

    let version_ids: Vec<i32> = query.load(&conn).expect("error loading version ids");

    let total_versions = version_ids.len();
    println!("Rendering {} versions", total_versions);

    let page_size = opts.page_size;

    let total_pages = total_versions / page_size;
    let total_pages = if total_versions % page_size == 0 {
        total_pages
    } else {
        total_pages + 1
    };

    let client = Client::new();

    for (page_num, version_ids_chunk) in version_ids.chunks(page_size).enumerate() {
        println!(
            "= Page {} of {} ==================================",
            page_num + 1,
            total_pages
        );

        let versions: Vec<(Version, String)> = versions::table
            .inner_join(crates::table)
            .filter(versions::id.eq(any(version_ids_chunk)))
            .select((versions::all_columns, crates::name))
            .load(&conn)
            .expect("error loading versions");

        let mut tasks = Vec::with_capacity(page_size as usize);
        for (version, krate_name) in versions {
            Version::record_readme_rendering(version.id, &conn).unwrap_or_else(|_| {
                panic!(
                    "[{}-{}] Couldn't record rendering time",
                    krate_name, version.num
                )
            });
            let client = client.clone();
            let base_config = base_config.clone();
            let handle = thread::spawn::<_, anyhow::Result<()>>(move || {
                println!("[{}-{}] Rendering README...", krate_name, version.num);
                let readme = get_readme(base_config.uploader(), &client, &version, &krate_name)?;
                let content_length = readme.len() as u64;
                let content = std::io::Cursor::new(readme);
                let readme_path = format!("readmes/{0}/{0}-{1}.html", krate_name, version.num);
                let mut extra_headers = header::HeaderMap::new();
                extra_headers.insert(
                    header::CACHE_CONTROL,
                    header::HeaderValue::from_static(CACHE_CONTROL_README),
                );

                base_config
                    .uploader()
                    .upload(
                        &client,
                        &readme_path,
                        content,
                        content_length,
                        "text/html",
                        extra_headers,
                    )
                    .context("Failed to upload rendered README file to S3")?;

                Ok(())
            });
            tasks.push(handle);
        }
        for handle in tasks {
            match handle.join() {
                Err(err) => println!("Thread panicked: {:?}", err),
                Ok(Err(err)) => println!("Thread failed: {:?}", err),
                _ => {}
            }
        }
    }
}

/// Renders the readme of an uploaded crate version.
fn get_readme(
    uploader: &Uploader,
    client: &Client,
    version: &Version,
    krate_name: &str,
) -> anyhow::Result<String> {
    let pkg_name = format!("{}-{}", krate_name, version.num);

    let location = uploader.crate_location(krate_name, &version.num.to_string());

    let location = match uploader {
        Uploader::S3 { .. } => location,
        Uploader::Local => format!("http://localhost:8888/{}", location),
    };

    let mut extra_headers = header::HeaderMap::new();
    extra_headers.insert(
        header::USER_AGENT,
        header::HeaderValue::from_static(USER_AGENT),
    );
    let request = client.get(&location).headers(extra_headers);
    let response = request.send().context("Failed to fetch crate")?;

    if !response.status().is_success() {
        return Err(anyhow!(
            "Failed to get a 200 response: {}",
            response.text().unwrap()
        ));
    }

    let reader = GzDecoder::new(response);
    let archive = Archive::new(reader);
    render_pkg_readme(archive, &pkg_name)
}

fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> anyhow::Result<String> {
    let mut entries = archive.entries().context("Invalid tar archive entries")?;

    let manifest: Manifest = {
        let path = format!("{}/Cargo.toml", pkg_name);
        let contents = find_file_by_path(&mut entries, Path::new(&path))
            .context("Failed to read Cargo.toml file")?;

        toml::from_str(&contents).context("Failed to parse manifest file")?
    };

    let rendered = {
        let readme_path = manifest
            .package
            .readme
            .clone()
            .unwrap_or_else(|| "README.md".into());
        let path = format!("{}/{}", pkg_name, readme_path);
        let contents = find_file_by_path(&mut entries, Path::new(&path))
            .with_context(|| format!("Failed to read {} file", readme_path))?;

        text_to_html(
            &contents,
            &readme_path,
            manifest.package.repository.as_deref(),
        )
    };
    return Ok(rendered);

    #[derive(Debug, Deserialize)]
    struct Package {
        readme: Option<String>,
        repository: Option<String>,
    }

    #[derive(Debug, Deserialize)]
    struct Manifest {
        package: Package,
    }
}

/// Search an entry by its path in a Tar archive.
fn find_file_by_path<R: Read>(
    entries: &mut tar::Entries<'_, R>,
    path: &Path,
) -> anyhow::Result<String> {
    let mut file = entries
        .filter_map(|entry| entry.ok())
        .find(|file| match file.path() {
            Ok(p) => p == path,
            Err(_) => false,
        })
        .ok_or_else(|| anyhow!("Failed to find tarball entry: {}", path.display()))?;

    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .context("Failed to read file contents")?;

    Ok(contents)
}

#[cfg(test)]
pub mod tests {
    use std::io::Write;
    use tar;

    use super::render_pkg_readme;

    pub fn add_file<W: Write>(pkg: &mut tar::Builder<W>, path: &str, content: &[u8]) {
        let mut header = tar::Header::new_gnu();
        header.set_size(content.len() as u64);
        header.set_cksum();
        pkg.append_data(&mut header, path, content).unwrap();
    }

    #[test]
    fn test_render_pkg_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "README.md"
"#,
        );
        add_file(&mut pkg, "foo-0.0.1/README.md", b"readme");
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("readme"))
    }

    #[test]
    fn test_render_pkg_no_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
"#,
        );
        let serialized_archive = pkg.into_inner().unwrap();
        assert_err!(render_pkg_readme(
            tar::Archive::new(&*serialized_archive),
            "foo-0.0.1"
        ));
    }

    #[test]
    fn test_render_pkg_implicit_readme() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
"#,
        );
        add_file(&mut pkg, "foo-0.0.1/README.md", b"readme");
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("readme"))
    }

    #[test]
    fn test_render_pkg_readme_w_link() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "README.md"
repository = "https://github.com/foo/foo"
"#,
        );
        add_file(
            &mut pkg,
            "foo-0.0.1/README.md",
            b"readme [link](./Other.md)",
        );
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/./Other.md\""))
    }

    #[test]
    fn test_render_pkg_readme_not_at_root() {
        let mut pkg = tar::Builder::new(vec![]);
        add_file(
            &mut pkg,
            "foo-0.0.1/Cargo.toml",
            br#"
[package]
readme = "docs/README.md"
repository = "https://github.com/foo/foo"
"#,
        );
        add_file(
            &mut pkg,
            "foo-0.0.1/docs/README.md",
            b"docs/readme [link](./Other.md)",
        );
        let serialized_archive = pkg.into_inner().unwrap();
        let result =
            render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
        assert!(result.contains("docs/readme"));
        assert!(result.contains("\"https://github.com/foo/foo/blob/HEAD/docs/./Other.md\""))
    }
}
<|endoftext|>


<commit_before>//! Defines valid types.

use std::fmt;
use std::collections::HashMap;

use petgraph::graph::{Graph, NodeIndex, EdgeIndex};

#[derive(Clone, Debug)]
pub enum RType {
    Overdefined,
    Reg8,
    Reg16,
    Reg32,
    Reg64,
    Num8,
    Num16,
    Num32,
    Num64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Int8,
    Int16,
    Int32,
    Int64,
    Bool,
    Ptr(Box<RType>),
    Code,
    Union(Vec<RType>),
    Intersect(Vec<RType>),
    Undefined,
}

impl fmt::Display for RType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let RType::Ptr(ref ty) = *self {
            write!(f, "{}*", ty)
        } else if let RType::Union(ref tys) = *self {
            let mut s = String::from("union(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else if let RType::Intersect(ref tys) = *self {
            let mut s = String::from("intersect(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else {
            let s = match *self {
                RType::Overdefined => "overdefined",
                RType::Reg8 => "reg8",
                RType::Reg16 => "reg16",
                RType::Reg32 => "reg32",
                RType::Reg64 => "reg64",
                RType::Num8 => "n8",
                RType::Num16 => "n16",
                RType::Num32 => "n32",
                RType::Num64 => "n64",
                RType::UInt8 => "u8",
                RType::UInt16 => "u16",
                RType::UInt32 => "u32",
                RType::UInt64 => "u64",
                RType::Int8 => "i8",
                RType::Int16 => "i16",
                RType::Int32 => "i32",
                RType::Int64 => "i64",
                RType::Bool => "bool",
                RType::Undefined => "underdefined",
                RType::Code => "code",
                _ => unreachable!(),
            };
            write!(f, "{}", s)
        }
    }
}

#[derive(Clone, Debug)]
pub enum ConstraintNode {
    Type(RType),
    AbstractType,
    TypeVar,
    DisjunctiveJoin,
    ConjunctiveJoin,
    Union,
    Intersect,
}

const EQUAL_EDGE: ConstraintEdge = ConstraintEdge::Equal;
const SUBTYPE_EDGE: ConstraintEdge = ConstraintEdge::SubType;

#[derive(Clone, Debug)]
pub enum ConstraintEdge {
    EdgeIdx(u8),
    SubType,
    Equal,
}

#[derive(Clone, Debug)]
pub enum SubTypeNode {
    // Index in the original constaints set.
    OrigIdx(NodeIndex),
}

#[derive(Clone, Debug)]
pub enum SubTypeEdge {
    SubType
}

#[derive(Clone, Debug)]
pub struct SubTypeSet {
    g: Graph<SubTypeNode, SubTypeEdge>,
    // Map from NodeIndex in ConstraintSet to NodeIndex in SubTypeSet.
    map: HashMap<NodeIndex, NodeIndex>,
}

impl SubTypeSet {
    pub fn new() -> SubTypeSet {
        SubTypeSet {
            g: Graph::new(),
            map: HashMap::new(),
        }
    }

    // Insert LHS <: RHS
    pub fn insert_relation(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        unimplemented!()
    }

    // For all ( alpha <: S ) return alpha
    pub fn subtypes_of(&self, idx_s: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }

    // For all ( T <: beta ) return beta
    pub fn supertypes_of(&self, idx_t: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }
}

#[derive(Clone, Debug)]
pub struct ConstraintSet {
    g: Graph<ConstraintNode, ConstraintEdge>,
    type_vars: HashMap<String, NodeIndex>,
    // S<:
    subty: SubTypeSet,
    // S=
    binding_map: HashMap<NodeIndex, RType>,
    // B^
    upper_bound: HashMap<NodeIndex, RType>,
    // B_
    lower_bound: HashMap<NodeIndex, RType>,
}

impl ConstraintSet {
    pub fn new() -> ConstraintSet {
        ConstraintSet {
            g: Graph::new(),
            type_vars: HashMap::new(),
            subty: SubTypeSet::new(),
            binding_map: HashMap::new(),
            upper_bound: HashMap::new(),
            lower_bound: HashMap::new(),
        }
    }

    pub fn operands(&self, n: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }

    pub fn insert_type_var(&mut self, named: Option<String>) -> NodeIndex {
        let var = self.g.add_node(ConstraintNode::TypeVar);
        let name = named.unwrap_or_else(|| format!("var_{}", self.type_vars.keys().len()));
        self.type_vars.insert(name, var);
        var
    }

    pub fn insert_abstract_type(&mut self) -> NodeIndex {
        self.g.add_node(ConstraintNode::AbstractType)
    }

    pub fn insert_base_type(&mut self, ty: RType) -> NodeIndex {
        self.g.add_node(ConstraintNode::Type(ty))
    }

    pub fn subtype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        self.g.update_edge(*lhs, *rhs, SUBTYPE_EDGE);
    }

    pub fn supertype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        self.subtype(rhs, lhs)
    }

    pub fn disjunctive_join(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let disjunction = self.g.add_node(ConstraintNode::DisjunctiveJoin);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(disjunction, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        disjunction
    }

    pub fn conjunctive_join(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let conjunction = self.g.add_node(ConstraintNode::ConjunctiveJoin);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(conjunction, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        conjunction
    }

    pub fn equal(&mut self, operands: &[NodeIndex; 2]) {
        self.g.update_edge(operands[0], operands[1], EQUAL_EDGE);
    }

    pub fn union(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn intersect(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        unimplemented!()
    }

    pub fn solve(&mut self) {
        unimplemented!()
    }

    fn decompose(&mut self, subtype_edge: EdgeIndex) {
        unimplemented!()
    }

    fn meet(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }

    fn join(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }
}
<commit_msg>Implements decompose<commit_after>//! Defines valid types.

use std::fmt;
use std::collections::HashMap;

use petgraph::graph::{EdgeIndex, Graph, NodeIndex};
use petgraph::EdgeDirection;

#[derive(Clone, Debug)]
pub enum RType {
    Overdefined,
    Reg8,
    Reg16,
    Reg32,
    Reg64,
    Num8,
    Num16,
    Num32,
    Num64,
    UInt8,
    UInt16,
    UInt32,
    UInt64,
    Int8,
    Int16,
    Int32,
    Int64,
    Bool,
    Ptr(Box<RType>),
    Code,
    Union(Vec<RType>),
    Intersect(Vec<RType>),
    Undefined,
}

impl fmt::Display for RType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let RType::Ptr(ref ty) = *self {
            write!(f, "{}*", ty)
        } else if let RType::Union(ref tys) = *self {
            let mut s = String::from("union(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else if let RType::Intersect(ref tys) = *self {
            let mut s = String::from("intersect(");
            let len = tys.len();
            for (i, ty) in tys.iter().enumerate() {
                s.push_str(&ty.to_string());
                if i < len - 1 {
                    s.push_str(", ");
                }
            }
            write!(f, "{})", s)
        } else {
            let s = match *self {
                RType::Overdefined => "overdefined",
                RType::Reg8 => "reg8",
                RType::Reg16 => "reg16",
                RType::Reg32 => "reg32",
                RType::Reg64 => "reg64",
                RType::Num8 => "n8",
                RType::Num16 => "n16",
                RType::Num32 => "n32",
                RType::Num64 => "n64",
                RType::UInt8 => "u8",
                RType::UInt16 => "u16",
                RType::UInt32 => "u32",
                RType::UInt64 => "u64",
                RType::Int8 => "i8",
                RType::Int16 => "i16",
                RType::Int32 => "i32",
                RType::Int64 => "i64",
                RType::Bool => "bool",
                RType::Undefined => "underdefined",
                RType::Code => "code",
                _ => unreachable!(),
            };
            write!(f, "{}", s)
        }
    }
}

#[derive(Clone, Debug)]
pub enum ConstraintNode {
    Type(RType),
    AbstractType,
    Ptr,
    TypeVar,
    DisjunctiveJoin,
    ConjunctiveJoin,
    Union,
    Intersect,
}

const EQUAL_EDGE: ConstraintEdge = ConstraintEdge::Equal;
const SUBTYPE_EDGE: ConstraintEdge = ConstraintEdge::SubType;

#[derive(Clone, Debug)]
pub enum ConstraintEdge {
    EdgeIdx(u8),
    SubType,
    Equal,
    Ptr,
}

#[derive(Clone, Debug)]
pub enum SubTypeNode {
    // Index in the original constaints set.
    OrigIdx(NodeIndex),
}

#[derive(Clone, Debug)]
pub enum SubTypeEdge {
    SubType,
}

#[derive(Clone, Debug)]
pub struct SubTypeSet {
    g: Graph<SubTypeNode, SubTypeEdge>,
    // Map from NodeIndex in ConstraintSet to NodeIndex in SubTypeSet.
    map: HashMap<NodeIndex, NodeIndex>,
}

impl SubTypeSet {
    pub fn new() -> SubTypeSet {
        SubTypeSet {
            g: Graph::new(),
            map: HashMap::new(),
        }
    }

    // Insert LHS <: RHS
    pub fn insert_relation(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        unimplemented!()
    }

    // For all ( alpha <: S ) return alpha
    pub fn subtypes_of(&self, idx_s: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }

    // For all ( T <: beta ) return beta
    pub fn supertypes_of(&self, idx_t: &NodeIndex) -> Vec<NodeIndex> {
        unimplemented!()
    }
}

#[derive(Clone, Debug)]
pub struct ConstraintSet {
    g: Graph<ConstraintNode, ConstraintEdge>,
    type_vars: HashMap<String, NodeIndex>,
    // S<:
    subty: SubTypeSet,
    // S=
    binding_map: HashMap<NodeIndex, RType>,
    // B^
    upper_bound: HashMap<NodeIndex, RType>,
    // B_
    lower_bound: HashMap<NodeIndex, RType>,
}

impl ConstraintSet {
    pub fn new() -> ConstraintSet {
        ConstraintSet {
            g: Graph::new(),
            type_vars: HashMap::new(),
            subty: SubTypeSet::new(),
            binding_map: HashMap::new(),
            upper_bound: HashMap::new(),
            lower_bound: HashMap::new(),
        }
    }

    pub fn operands(&self, n: &NodeIndex) -> Vec<NodeIndex> {
        let mut result = Vec::new();
        for (n, e) in self.g.edges_directed(*n, EdgeDirection::Outgoing) {
            if let ConstraintEdge::EdgeIdx(i) = *e {
                result.push((i, n));
            }
        }
        result.sort_by(|a, b| a.0.cmp(&b.0));
        result.iter().map(|a| a.1).collect()
    }

    pub fn insert_type_var(&mut self, named: Option<String>) -> NodeIndex {
        let var = self.g.add_node(ConstraintNode::TypeVar);
        let name = named.unwrap_or_else(|| format!("var_{}", self.type_vars.keys().len()));
        self.type_vars.insert(name, var);
        var
    }

    pub fn insert_abstract_type(&mut self) -> NodeIndex {
        self.g.add_node(ConstraintNode::AbstractType)
    }

    pub fn insert_base_type(&mut self, ty: RType) -> NodeIndex {
        self.g.add_node(ConstraintNode::Type(ty))
    }

    pub fn subtype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        self.g.update_edge(*lhs, *rhs, SUBTYPE_EDGE);
    }

    pub fn supertype(&mut self, lhs: &NodeIndex, rhs: &NodeIndex) {
        self.subtype(rhs, lhs)
    }

    pub fn disjunctive_join(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let disjunction = self.g.add_node(ConstraintNode::DisjunctiveJoin);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(disjunction, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        disjunction
    }

    pub fn conjunctive_join(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let conjunction = self.g.add_node(ConstraintNode::ConjunctiveJoin);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(conjunction, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        conjunction
    }

    pub fn equal(&mut self, operands: &[NodeIndex; 2]) {
        self.g.update_edge(operands[0], operands[1], EQUAL_EDGE);
    }

    pub fn union(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let union = self.g.add_node(ConstraintNode::Union);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(union, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        union
    }

    pub fn intersect(&mut self, operands: &[NodeIndex]) -> NodeIndex {
        let intersection = self.g.add_node(ConstraintNode::Intersect);
        for (i, idx) in operands.iter().enumerate() {
            self.g.update_edge(intersection, *idx, ConstraintEdge::EdgeIdx(i as u8));
        }
        intersection
    }

    pub fn solve(&mut self) {
        unimplemented!()
    }

    fn inner_type(&self, ptr_node: NodeIndex) -> NodeIndex {
        self.g
            .edges_directed(ptr_node, EdgeDirection::Outgoing)
            .find(|x| {
                match *x.1 {
                    ConstraintEdge::Ptr => true,
                    _ => false,
                }
            })
            .expect("Inner type of `Ptr` cannot be `None`")
            .0
    }

    // This function decomposes the first node and adds the appropriate edges to
    // the other node.
    fn decompose_constraint_node(&mut self, lhs: NodeIndex, rhs: NodeIndex) {
        let rhs_is_ptr = match self.g[rhs] {
            ConstraintNode::Intersect => {
                let operands = self.operands(&rhs);
                for op in &operands {
                    self.g.update_edge(lhs, *op, ConstraintEdge::SubType);
                }
                false
            }
            ConstraintNode::Ptr => true,
            _ => false,
        };

        let lhs_is_ptr = match self.g[lhs] {
            ConstraintNode::Union => {
                let lhs_operands = self.operands(&lhs);
                let mut subtype_relations = Vec::new();
                let mut edges = self.g.neighbors_directed(lhs, EdgeDirection::Incoming).detach();
                while let Some(edge) = edges.next_edge(&self.g) {
                    if let ConstraintEdge::SubType = self.g[edge] {
                        let (_, target) = self.g.edge_endpoints(edge).expect("");
                        self.g.remove_edge(edge);
                        subtype_relations.push(target);
                    }
                }
                for rhs_op in subtype_relations {
                    for lhs_op in &lhs_operands {
                        self.g.update_edge(*lhs_op, rhs_op, ConstraintEdge::SubType);
                    }
                }
                false
            }
            ConstraintNode::Ptr => true,
            _ => false,
        };

        if lhs_is_ptr && rhs_is_ptr {
            let lhs_inner = self.inner_type(lhs);
            let rhs_inner = self.inner_type(rhs);
            self.g.update_edge(lhs_inner, rhs_inner, ConstraintEdge::SubType);
        }
    }

    fn decompose_subtype_relation(&mut self, subtype_edge: EdgeIndex) {
        // According to section 6.3.2 - Decomposition Rules.
        let (lhs, rhs) = self.g.edge_endpoints(subtype_edge).expect("This cannot be None");
        self.decompose_constraint_node(lhs, rhs);
    }

    fn meet(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }

    fn join(&mut self, operands: &[RType]) -> RType {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>extern crate sodiumoxide;
extern crate num_traits;
extern crate env_logger;
extern crate godcoin;
extern crate tokio;
extern crate ctrlc;
extern crate dirs;
extern crate clap;

#[macro_use]
extern crate log;

use clap::{Arg, App, AppSettings, SubCommand};
use std::sync::{Arc, mpsc};
use tokio::prelude::*;
use godcoin::*;

struct StartNode<'a> {
    bind_address: Option<&'a str>,
    minter_key: Option<KeyPair>
}

fn generate_keypair(shutdown_handle: mpsc::Sender<()>) {
    let pair = KeyPair::gen_keypair();
    info!("~~ Keys have been generated ~~");
    info!("Private key WIF: {}", pair.1.to_wif());
    info!("Public key WIF: {}", pair.0.to_wif());
    info!("- Make sure the keys are securely stored");
    info!("- Coins cannot be recovered if you lose your private key");
    info!("- Never give private keys to anyone");
    shutdown_handle.send(()).unwrap();
}

fn start_node(node_opts: StartNode) {
    use godcoin::blockchain::*;
    use std::{env, path::*};

    let home: PathBuf = {
        use dirs;
        let home = env::var("GODCOIN_HOME").map(|s| {
            PathBuf::from(s)
        }).unwrap_or_else(|_| {
            Path::join(&dirs::data_local_dir().unwrap(), "godcoin")
        });
        if !Path::is_dir(&home) {
            let res = std::fs::create_dir(&home);
            res.expect(&format!("Failed to create dir at {:?}", &home));
            info!("Created GODcoin home at {:?}", &home);
        } else {
            info!("Found GODcoin home at {:?}", &home);
        }
        home
    };

    let mut blockchain = Blockchain::new(&home);
    {
        let create_genesis = blockchain.get_block(0).is_none();
        if create_genesis && node_opts.minter_key.is_some() {
            if let Some(ref key) = node_opts.minter_key {
                blockchain.create_genesis_block(key);
            }
        }
    }

    info!("Using height in block log at {}", blockchain.get_chain_height());

    if let Some(ref key) = node_opts.minter_key {
        let bond = blockchain.get_bond(&key.0).expect("No bond found for minter key");
        let minter = key.clone();
        let staker = bond.staker;
        let producer = producer::Producer::new(Arc::new(blockchain), minter, staker);
        producer.start_timer();
    }

    if let Some(bind) = node_opts.bind_address {
        let addr = bind.parse()
                        .expect(&format!("Failed to parse address: {:?}", bind));
        net::server::start(&addr);
    }
}

fn main() {
    let env = env_logger::Env::new().filter_or(env_logger::DEFAULT_FILTER_ENV, "godcoin=info");
    env_logger::init_from_env(env);

    godcoin::init().unwrap();
    let app = App::new("godcoin")
                .about("GODcoin core CLI")
                .version(env!("CARGO_PKG_VERSION"))
                .setting(AppSettings::VersionlessSubcommands)
                .setting(AppSettings::SubcommandRequiredElseHelp)
                .subcommand(SubCommand::with_name("keygen")
                            .about("Generates a keypair"))
                .subcommand(SubCommand::with_name("node")
                            .about("Starts the blockchain node service")
                            .arg(Arg::with_name("bind_address")
                                .help("Bind address endpoint (i.e 0.0.0.0:7777)")
                                .long("bind")
                                .value_name("address"))
                            .arg(Arg::with_name("minter_key")
                                .help("Private minting key required to mint")
                                .long("minter-key")
                                .value_name("key")));
    let matches = app.get_matches();

    let (tx, rx) = mpsc::channel::<()>();
    let mut rt = tokio::runtime::Runtime::new().unwrap();

    {
        let tx = tx.clone();
        rt.block_on(future::lazy(move || {
            use ::std::io::{Error, ErrorKind};

            if let Some(_) = matches.subcommand_matches("keygen") {
                generate_keypair(tx);
            } else if let Some(matches) = matches.subcommand_matches("node") {
                start_node(StartNode {
                    bind_address: matches.value_of("bind_address"),
                    minter_key: matches.value_of("minter_key").map(|s| {
                        godcoin::PrivateKey::from_wif(s)
                            .expect("Failed to parse minter key argument")
                    })
                });
            } else {
                return Err(Error::new(ErrorKind::Other, "Failed to match subcommand"))
            }

            Ok(())
        }).map_err(|err| {
            error!("Startup failure: {:?}", err);
        })).unwrap();
    }

    ctrlc::set_handler(move || {
        println!("Received ctrl-c signal, shutting down...");
        tx.send(()).unwrap();
    }).unwrap();

    rx.recv().unwrap();
    rt.shutdown_now().wait().ok().unwrap();
}
<commit_msg>Fix clippy warnings<commit_after>extern crate sodiumoxide;
extern crate num_traits;
extern crate env_logger;
extern crate godcoin;
extern crate tokio;
extern crate ctrlc;
extern crate dirs;
extern crate clap;

#[macro_use]
extern crate log;

use clap::{Arg, App, AppSettings, SubCommand};
use std::sync::{Arc, mpsc};
use tokio::prelude::*;
use godcoin::*;

struct StartNode<'a> {
    bind_address: Option<&'a str>,
    minter_key: Option<KeyPair>
}

fn generate_keypair(shutdown_handle: &mpsc::Sender<()>) {
    let pair = KeyPair::gen_keypair();
    info!("~~ Keys have been generated ~~");
    info!("Private key WIF: {}", pair.1.to_wif());
    info!("Public key WIF: {}", pair.0.to_wif());
    info!("- Make sure the keys are securely stored");
    info!("- Coins cannot be recovered if you lose your private key");
    info!("- Never give private keys to anyone");
    shutdown_handle.send(()).unwrap();
}

fn start_node(node_opts: &StartNode) {
    use godcoin::blockchain::*;
    use std::{env, path::*};

    let home: PathBuf = {
        use dirs;
        let home = env::var("GODCOIN_HOME").map(|s| {
            PathBuf::from(s)
        }).unwrap_or_else(|_| {
            Path::join(&dirs::data_local_dir().unwrap(), "godcoin")
        });
        if !Path::is_dir(&home) {
            let res = std::fs::create_dir(&home);
            res.unwrap_or_else(|_| panic!("Failed to create dir at {:?}", &home));
            info!("Created GODcoin home at {:?}", &home);
        } else {
            info!("Found GODcoin home at {:?}", &home);
        }
        home
    };

    let mut blockchain = Blockchain::new(&home);
    {
        let create_genesis = blockchain.get_block(0).is_none();
        if create_genesis && node_opts.minter_key.is_some() {
            if let Some(ref key) = node_opts.minter_key {
                blockchain.create_genesis_block(key);
            }
        }
    }

    info!("Using height in block log at {}", blockchain.get_chain_height());

    if let Some(ref key) = node_opts.minter_key {
        let bond = blockchain.get_bond(&key.0).expect("No bond found for minter key");
        let minter = key.clone();
        let staker = bond.staker;
        let producer = producer::Producer::new(Arc::new(blockchain), minter, staker);
        producer.start_timer();
    }

    if let Some(bind) = node_opts.bind_address {
        let addr = bind.parse()
                        .unwrap_or_else(|_| panic!("Failed to parse address: {:?}", bind));
        net::server::start(&addr);
    }
}

fn main() {
    let env = env_logger::Env::new().filter_or(env_logger::DEFAULT_FILTER_ENV, "godcoin=info");
    env_logger::init_from_env(env);

    godcoin::init().unwrap();
    let app = App::new("godcoin")
                .about("GODcoin core CLI")
                .version(env!("CARGO_PKG_VERSION"))
                .setting(AppSettings::VersionlessSubcommands)
                .setting(AppSettings::SubcommandRequiredElseHelp)
                .subcommand(SubCommand::with_name("keygen")
                            .about("Generates a keypair"))
                .subcommand(SubCommand::with_name("node")
                            .about("Starts the blockchain node service")
                            .arg(Arg::with_name("bind_address")
                                .help("Bind address endpoint (i.e 0.0.0.0:7777)")
                                .long("bind")
                                .value_name("address"))
                            .arg(Arg::with_name("minter_key")
                                .help("Private minting key required to mint")
                                .long("minter-key")
                                .value_name("key")));
    let matches = app.get_matches();

    let (tx, rx) = mpsc::channel::<()>();
    let mut rt = tokio::runtime::Runtime::new().unwrap();

    {
        let tx = tx.clone();
        rt.block_on(future::lazy(move || {
            use ::std::io::{Error, ErrorKind};

            if matches.subcommand_matches("keygen").is_some() {
                generate_keypair(&tx);
            } else if let Some(matches) = matches.subcommand_matches("node") {
                start_node(&StartNode {
                    bind_address: matches.value_of("bind_address"),
                    minter_key: matches.value_of("minter_key").map(|s| {
                        godcoin::PrivateKey::from_wif(s)
                            .expect("Failed to parse minter key argument")
                    })
                });
            } else {
                return Err(Error::new(ErrorKind::Other, "Failed to match subcommand"))
            }

            Ok(())
        }).map_err(|err| {
            error!("Startup failure: {:?}", err);
        })).unwrap();
    }

    ctrlc::set_handler(move || {
        println!("Received ctrl-c signal, shutting down...");
        tx.send(()).unwrap();
    }).unwrap();

    rx.recv().unwrap();
    rt.shutdown_now().wait().ok().unwrap();
}
<|endoftext|>