<commit_before>use evalbotlib::{util, EvalService, Language};

use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;

use futures::StreamExt;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use telegram_bot::*;
use tokio::sync::RwLock;

static WHITELIST_FILENAME: &'static str = "tgwhitelist.toml";

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct TgCfg {
    owners: HashSet<i64>,
    msg_owner_id: Option<i64>,
    bot_id: String,
    lang_subst: HashMap<String, String>,
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct TgWhitelist {
    priv_enabled: bool,
    group_enabled: bool,
    allowed: HashSet<i64>,
    blocked: HashSet<i64>,
}

impl TgWhitelist {
    fn priv_ok(&self, id: ChatId) -> bool {
        (!self.priv_enabled || self.allowed.contains(&id.into()))
            && !self.blocked.contains(&id.into())
    }

    fn group_ok(&self, id: ChatId) -> bool {
        (!self.group_enabled || self.allowed.contains(&id.into()))
            && !self.blocked.contains(&id.into())
    }

    fn allow(&mut self, id: i64) {
        self.allowed.insert(id);
    }

    fn unallow(&mut self, id: i64) {
        self.allowed.remove(&id);
    }

    fn block(&mut self, id: i64) {
        self.blocked.insert(id);
    }

    fn unblock(&mut self, id: i64) {
        self.blocked.remove(&id);
    }

    async fn save<'a>(&'a self, path: &'static str) {
        drop(util::encode(self, path).await);
    }
}

struct TgSvc {
    config: TgCfg,
    whitelist: RwLock<TgWhitelist>,
    service: EvalService,
    api: Api,
    bot_user: User,
}

fn telegram_wrap_result(s: &str, group: bool) -> String {
    // FIXME configurable max-lines and max-bytes
    if s.is_empty() {
        "no output".to_owned()
    } else {
        let mut r = "
".to_owned();
        let input = s.as_bytes();
        let mut cut_input = String::from_utf8_lossy(&input[..512.min(input.len())]);
        if group {
            cut_input = Cow::Owned(cut_input.lines().take(10).collect::<Vec<_>>().join("\n"));
        }
        r.push_str(
            &cut_input
                .replace(
                    |c: char| c == '\u{FFFD}' || (c.is_control() && c != '\n' && c != '\t'),
                    "",
                )
                .replace('&', "&")
                .replace('<', "<")
                .replace('>', ">")
                .replace('"', """),
        );
        r.push_str("
");
        if cut_input.len() + 1 // we also cut off the trailing \n
            < input.len()
        {
            r.push_str("... (truncated)");
        }
        r
    }
}

fn is_from_owner(msg: &Message, tgsvc: &TgSvc) -> bool {
    tgsvc.is_owner(msg.from.to_user_id())
}

async fn verify_allowed(chat: &MessageChat, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    let group = if let MessageChat::Private(_) = chat {
        false
    } else {
        true
    };
    let wl = tgsvc.whitelist.read().await;
    let chat_id = chat.id();
    if group && !wl.group_ok(chat_id) || !group && !wl.priv_ok(chat_id) {
        let send_message = tgsvc.api.send(SendMessage::new(
            &chat,
            format!(
                "You or this group is not on the whitelist. Seek help. ID: {}",
                chat_id
            ),
        ));
        let leave_group = if group {
            Some(tgsvc.api.send(LeaveChat::new(&chat)))
        } else {
            None
        };
        tokio::spawn(async move {
            drop(send_message.await.map_err(|e| {
                warn!("failed to send message: {}", e);
            }));
            if let Some(leave_group) = leave_group {
                drop(leave_group.await.map_err(|e| {
                    warn!("failed to leave group {}: {}", chat_id, e);
                }));
            }
        });
        Err(())
    } else {
        Ok(())
    }
}

async fn handle_update(update: Update, tgsvc: Arc<TgSvc>) -> Result<(), ()> {
    let message = match update {
        Update {
            kind: UpdateKind::Message(message),
            ..
        } => message,
        _ => return Ok(()),
    };

    match message.kind {
        MessageKind::GroupChatCreated => handle_group_created(message.chat, &tgsvc).await?,
        MessageKind::NewChatMembers { data } => {
            handle_new_chat_members(message.chat, data, &tgsvc).await?
        }
        MessageKind::Text { .. } => handle_message(message, &tgsvc).await?,
        _ => (),
    };

    Ok(())
}

async fn handle_new_chat_members(
    chat: MessageChat,
    new_members: Vec<User>,
    tgsvc: &Arc<TgSvc>,
) -> Result<(), ()> {
    if new_members.iter().any(|u| u.id == tgsvc.bot_user.id) {
        verify_allowed(&chat, tgsvc).await?;
    }

    Ok(())
}

async fn handle_group_created(chat: MessageChat, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    verify_allowed(&chat, tgsvc).await?;
    Ok(())
}

async fn handle_message(message: Message, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    let text = if let MessageKind::Text { ref data, .. } = message.kind {
        data
    } else {
        return Ok(());
    };

    if !text.starts_with("/") {
        return Ok(());
    }

    let cmd = if let Some(cmd) = text.split_whitespace().nth(0) {
        cmd
    } else {
        return Ok(());
    };

    let args = text[cmd.len()..].trim_start();

    match cmd {
        "/privwl" => {
            handle_whitelist_toggle(tgsvc, &message, WhitelistToggleOp::TogglePrivate).await?
        }
        "/groupwl" => {
            handle_whitelist_toggle(tgsvc, &message, WhitelistToggleOp::ToggleGroup).await?
        }
        "/allow" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Allow).await?,
        "/unallow" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Unallow).await?,
        "/block" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Block).await?,
        "/unblock" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Unblock).await?,
        "/leave" => handle_leave(tgsvc, &message, args).await?,
        _ => {
            let (cmd, is_hash) = if cmd.ends_with('#') {
                (&cmd[1..cmd.len() - 1], true)
            } else {
                (&cmd[1..], false)
            };
            if let Some((_, lang)) = tgsvc.service.langs().find(|(name, _)| *name == cmd) {
                handle_eval(tgsvc, &message, args, lang, is_hash).await?;
            }
        }
    }

    Ok(())
}

async fn handle_eval(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    args: &str,
    lang: &Arc<Language>,
    is_hash: bool,
) -> Result<(), ()> {
    verify_allowed(&msg.chat, tgsvc).await?;
    let no_limit = is_hash && is_from_owner(&msg, tgsvc);
    let is_group = if let MessageChat::Private(_) = msg.chat {
        false
    } else {
        true
    };
    let chat_id = msg.chat.id();
    let msg_id = msg.id;
    info!("({}) evaluating from {:?}: {:?}", msg_id, msg.from, args);
    let code = {
        let mut r = args.to_owned();
        r.push_str("\n");
        r
    };

    let eval_result = lang
        .eval(
            code,
            if no_limit { Some(0) } else { None },
            Some(format!("tg{}", chat_id)),
        )
        .await;
    let ok = eval_result.is_ok();
    info!(
        "({}) result: {:?}",
        msg_id,
        eval_result.as_ref().map(|x| x.as_str()).unwrap_or("")
    );
    let mut request = SendMessage::new(
        &msg.chat,
        eval_result
            .map(|r| telegram_wrap_result(&r, is_group))
            .unwrap_or_else(|e| e),
    );
    request.reply_to(msg);
    if ok {
        request.parse_mode(ParseMode::Html);
    }
    tokio::spawn(tgsvc.api.send(request));

    Ok(())
}

#[derive(Clone, Copy)]
enum WhitelistToggleOp {
    TogglePrivate,
    ToggleGroup,
}

#[derive(Clone, Copy)]
enum WhitelistModOp {
    Allow,
    Unallow,
    Block,
    Unblock,
}

async fn handle_whitelist_toggle(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    op: WhitelistToggleOp,
) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let mut wl = tgsvc.whitelist.write().await;
    let resp = match op {
        WhitelistToggleOp::TogglePrivate => {
            wl.priv_enabled = !wl.priv_enabled;
            wl.save(WHITELIST_FILENAME).await;
            format!("Private whitelist enabled: {}", wl.priv_enabled)
        }
        WhitelistToggleOp::ToggleGroup => {
            wl.group_enabled = !wl.group_enabled;
            wl.save(WHITELIST_FILENAME).await;
            format!("Group whitelist enabled: {}", wl.group_enabled)
        }
    };
    tokio::spawn(tgsvc.api.send(SendMessage::new(&msg.chat, resp)));
    Ok(())
}

async fn handle_whitelist_mod(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    args: &str,
    op: WhitelistModOp,
) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let arg = args
        .trim()
        .split_whitespace()
        .nth(0)
        .and_then(|arg| arg.parse().ok());
    let mut wl = tgsvc.whitelist.write().await;
    let resp = if let Some(id) = arg {
        match op {
            WhitelistModOp::Allow => {
                wl.allow(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Allowed {}", id)
            }
            WhitelistModOp::Unallow => {
                wl.unallow(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Unallowed {}", id)
            }
            WhitelistModOp::Block => {
                wl.block(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Blocked {}", id)
            }
            WhitelistModOp::Unblock => {
                wl.unblock(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Unblocked {}", id)
            }
        }
    } else {
        "Invalid ID".to_owned()
    };

    tokio::spawn(tgsvc.api.send(SendMessage::new(&msg.chat, resp)));
    Ok(())
}

async fn handle_leave(tgsvc: &Arc<TgSvc>, msg: &Message, args: &str) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let arg: Option<i64> = args
        .trim()
        .split_whitespace()
        .nth(0)
        .and_then(|arg| arg.parse().ok());
    let (maybe_leave_chat, resp) = match arg {
        Some(id) => (Some(tgsvc.api.send(LeaveChat::new(ChatId::from(id)))), "OK"),
        None => (None, "Invalid ID"),
    };

    let send_response = tgsvc.api.send(SendMessage::new(&msg.chat, resp));

    tokio::spawn(async move {
        if let Some(fut) = maybe_leave_chat {
            drop(fut.await.map_err(|e| warn!("failed to leave group: {}", e)));
        }
        drop(
            send_response
                .await
                .map_err(|e| warn!("failed to send message: {}", e)),
        );
    });
    Ok(())
}

impl TgSvc {
    async fn run() -> Result<(), ()> {
        let cfg = util::decode::<TgCfg, _>("evalbot.tg.toml")
            .await
            .map(|cfg| {
                debug!("Loaded config: {:?}", cfg);
                cfg
            })
            .map_err(|e| error!("failed to read evalbot.tg.toml: {}", e))?;
        let wl = util::decode::<TgWhitelist, _>(WHITELIST_FILENAME)
            .await
            .or_else(|e| {
                warn!("failed to read whitelist: {}; using empty whitelist", e);
                Ok(TgWhitelist {
                    priv_enabled: false,
                    group_enabled: false,
                    allowed: HashSet::new(),
                    blocked: HashSet::new(),
                })
            })?;

        let api = Api::new(&cfg.bot_id);
        let bot_user = api
            .send(GetMe)
            .await
            .map_err(|e| error!("failed to get bot's user info: {}", e))?;
        let service = EvalService::from_toml_file("evalbot.toml")
            .await
            .map_err(|e| error!("failed to read evalbot.toml: {}", e))?;
        TgSvc {
            api: Api::new(&cfg.bot_id),
            config: cfg,
            whitelist: RwLock::new(wl),
            service: service,
            bot_user: bot_user,
        }
        .handle()
        .await;
        Ok(())
    }

    async fn handle(self) {
        let me = Arc::new(self);

        let mut stream = me.api.stream();
        stream
            .timeout(Duration::from_secs(35))
            .error_delay(Duration::from_secs(30));
        while let Some(update) = stream.next().await {
            match update {
                Ok(update) => {
                    tokio::spawn(handle_update(update, me.clone()));
                }
                Err(error) => error!("received error: {:?}", error),
            }
        }
    }

    fn is_owner(&self, id: UserId) -> bool {
        self.config.owners.contains(&id.into())
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();
    drop(TgSvc::run().await);
}
<commit_msg>Deal with commands like /js@username<commit_after>use evalbotlib::{util, EvalService, Language};

use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;

use futures::StreamExt;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use telegram_bot::*;
use tokio::sync::RwLock;

static WHITELIST_FILENAME: &'static str = "tgwhitelist.toml";

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct TgCfg {
    owners: HashSet<i64>,
    msg_owner_id: Option<i64>,
    bot_id: String,
    lang_subst: HashMap<String, String>,
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
struct TgWhitelist {
    priv_enabled: bool,
    group_enabled: bool,
    allowed: HashSet<i64>,
    blocked: HashSet<i64>,
}

impl TgWhitelist {
    fn priv_ok(&self, id: ChatId) -> bool {
        (!self.priv_enabled || self.allowed.contains(&id.into()))
            && !self.blocked.contains(&id.into())
    }

    fn group_ok(&self, id: ChatId) -> bool {
        (!self.group_enabled || self.allowed.contains(&id.into()))
            && !self.blocked.contains(&id.into())
    }

    fn allow(&mut self, id: i64) {
        self.allowed.insert(id);
    }

    fn unallow(&mut self, id: i64) {
        self.allowed.remove(&id);
    }

    fn block(&mut self, id: i64) {
        self.blocked.insert(id);
    }

    fn unblock(&mut self, id: i64) {
        self.blocked.remove(&id);
    }

    async fn save<'a>(&'a self, path: &'static str) {
        drop(util::encode(self, path).await);
    }
}

struct TgSvc {
    config: TgCfg,
    whitelist: RwLock<TgWhitelist>,
    service: EvalService,
    api: Api,
    bot_user: User,
    username: String,
}

fn telegram_wrap_result(s: &str, group: bool) -> String {
    // FIXME configurable max-lines and max-bytes
    if s.is_empty() {
        "no output".to_owned()
    } else {
        let mut r = "
".to_owned();
        let input = s.as_bytes();
        let mut cut_input = String::from_utf8_lossy(&input[..512.min(input.len())]);
        if group {
            cut_input = Cow::Owned(cut_input.lines().take(10).collect::<Vec<_>>().join("\n"));
        }
        r.push_str(
            &cut_input
                .replace(
                    |c: char| c == '\u{FFFD}' || (c.is_control() && c != '\n' && c != '\t'),
                    "",
                )
                .replace('&', "&")
                .replace('<', "<")
                .replace('>', ">")
                .replace('"', """),
        );
        r.push_str("
");
        if cut_input.len() + 1 // we also cut off the trailing \n
            < input.len()
        {
            r.push_str("... (truncated)");
        }
        r
    }
}

fn is_from_owner(msg: &Message, tgsvc: &TgSvc) -> bool {
    tgsvc.is_owner(msg.from.to_user_id())
}

async fn verify_allowed(chat: &MessageChat, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    let group = if let MessageChat::Private(_) = chat {
        false
    } else {
        true
    };
    let wl = tgsvc.whitelist.read().await;
    let chat_id = chat.id();
    if group && !wl.group_ok(chat_id) || !group && !wl.priv_ok(chat_id) {
        let send_message = tgsvc.api.send(SendMessage::new(
            &chat,
            format!(
                "You or this group is not on the whitelist. Seek help. ID: {}",
                chat_id
            ),
        ));
        let leave_group = if group {
            Some(tgsvc.api.send(LeaveChat::new(&chat)))
        } else {
            None
        };
        tokio::spawn(async move {
            drop(send_message.await.map_err(|e| {
                warn!("failed to send message: {}", e);
            }));
            if let Some(leave_group) = leave_group {
                drop(leave_group.await.map_err(|e| {
                    warn!("failed to leave group {}: {}", chat_id, e);
                }));
            }
        });
        Err(())
    } else {
        Ok(())
    }
}

async fn handle_update(update: Update, tgsvc: Arc<TgSvc>) -> Result<(), ()> {
    let message = match update {
        Update {
            kind: UpdateKind::Message(message),
            ..
        } => message,
        _ => return Ok(()),
    };

    match message.kind {
        MessageKind::GroupChatCreated => handle_group_created(message.chat, &tgsvc).await?,
        MessageKind::NewChatMembers { data } => {
            handle_new_chat_members(message.chat, data, &tgsvc).await?
        }
        MessageKind::Text { .. } => handle_message(message, &tgsvc).await?,
        _ => (),
    };

    Ok(())
}

async fn handle_new_chat_members(
    chat: MessageChat,
    new_members: Vec<User>,
    tgsvc: &Arc<TgSvc>,
) -> Result<(), ()> {
    if new_members.iter().any(|u| u.id == tgsvc.bot_user.id) {
        verify_allowed(&chat, tgsvc).await?;
    }

    Ok(())
}

async fn handle_group_created(chat: MessageChat, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    verify_allowed(&chat, tgsvc).await?;
    Ok(())
}

async fn handle_message(message: Message, tgsvc: &Arc<TgSvc>) -> Result<(), ()> {
    let text = if let MessageKind::Text { ref data, .. } = message.kind {
        data
    } else {
        return Ok(());
    };

    if !text.starts_with("/") {
        return Ok(());
    }

    let (cmd, args) = {
        let first_tok = if let Some(first_tok) = text.split_whitespace().nth(0) {
            first_tok
        } else {
            return Ok(());
        };

        let mut split = first_tok.splitn(1, '@');
        let cmd = split.next();
        let user = split.next();

        if let Some(user) = user {
            if user != tgsvc.username {
                return Ok(());
            }
        }

        (
            cmd.expect("First split should exist"),
            text[first_tok.len()..].trim_start(),
        )
    };

    match cmd {
        "/privwl" => {
            handle_whitelist_toggle(tgsvc, &message, WhitelistToggleOp::TogglePrivate).await?
        }
        "/groupwl" => {
            handle_whitelist_toggle(tgsvc, &message, WhitelistToggleOp::ToggleGroup).await?
        }
        "/allow" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Allow).await?,
        "/unallow" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Unallow).await?,
        "/block" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Block).await?,
        "/unblock" => handle_whitelist_mod(tgsvc, &message, args, WhitelistModOp::Unblock).await?,
        "/leave" => handle_leave(tgsvc, &message, args).await?,
        _ => {
            let (cmd, is_hash) = if cmd.ends_with('#') {
                (&cmd[1..cmd.len() - 1], true)
            } else {
                (&cmd[1..], false)
            };
            if let Some((_, lang)) = tgsvc.service.langs().find(|(name, _)| *name == cmd) {
                handle_eval(tgsvc, &message, args, lang, is_hash).await?;
            }
        }
    }

    Ok(())
}

async fn handle_eval(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    args: &str,
    lang: &Arc<Language>,
    is_hash: bool,
) -> Result<(), ()> {
    verify_allowed(&msg.chat, tgsvc).await?;
    let no_limit = is_hash && is_from_owner(&msg, tgsvc);
    let is_group = if let MessageChat::Private(_) = msg.chat {
        false
    } else {
        true
    };
    let chat_id = msg.chat.id();
    let msg_id = msg.id;
    info!("({}) evaluating from {:?}: {:?}", msg_id, msg.from, args);
    let code = {
        let mut r = args.to_owned();
        r.push_str("\n");
        r
    };

    let eval_result = lang
        .eval(
            code,
            if no_limit { Some(0) } else { None },
            Some(format!("tg{}", chat_id)),
        )
        .await;
    let ok = eval_result.is_ok();
    info!(
        "({}) result: {:?}",
        msg_id,
        eval_result.as_ref().map(|x| x.as_str()).unwrap_or("")
    );
    let mut request = SendMessage::new(
        &msg.chat,
        eval_result
            .map(|r| telegram_wrap_result(&r, is_group))
            .unwrap_or_else(|e| e),
    );
    request.reply_to(msg);
    if ok {
        request.parse_mode(ParseMode::Html);
    }
    tokio::spawn(tgsvc.api.send(request));

    Ok(())
}

#[derive(Clone, Copy)]
enum WhitelistToggleOp {
    TogglePrivate,
    ToggleGroup,
}

#[derive(Clone, Copy)]
enum WhitelistModOp {
    Allow,
    Unallow,
    Block,
    Unblock,
}

async fn handle_whitelist_toggle(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    op: WhitelistToggleOp,
) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let mut wl = tgsvc.whitelist.write().await;
    let resp = match op {
        WhitelistToggleOp::TogglePrivate => {
            wl.priv_enabled = !wl.priv_enabled;
            wl.save(WHITELIST_FILENAME).await;
            format!("Private whitelist enabled: {}", wl.priv_enabled)
        }
        WhitelistToggleOp::ToggleGroup => {
            wl.group_enabled = !wl.group_enabled;
            wl.save(WHITELIST_FILENAME).await;
            format!("Group whitelist enabled: {}", wl.group_enabled)
        }
    };
    tokio::spawn(tgsvc.api.send(SendMessage::new(&msg.chat, resp)));
    Ok(())
}

async fn handle_whitelist_mod(
    tgsvc: &Arc<TgSvc>,
    msg: &Message,
    args: &str,
    op: WhitelistModOp,
) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let arg = args
        .trim()
        .split_whitespace()
        .nth(0)
        .and_then(|arg| arg.parse().ok());
    let mut wl = tgsvc.whitelist.write().await;
    let resp = if let Some(id) = arg {
        match op {
            WhitelistModOp::Allow => {
                wl.allow(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Allowed {}", id)
            }
            WhitelistModOp::Unallow => {
                wl.unallow(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Unallowed {}", id)
            }
            WhitelistModOp::Block => {
                wl.block(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Blocked {}", id)
            }
            WhitelistModOp::Unblock => {
                wl.unblock(id);
                wl.save(WHITELIST_FILENAME).await;
                format!("Unblocked {}", id)
            }
        }
    } else {
        "Invalid ID".to_owned()
    };

    tokio::spawn(tgsvc.api.send(SendMessage::new(&msg.chat, resp)));
    Ok(())
}

async fn handle_leave(tgsvc: &Arc<TgSvc>, msg: &Message, args: &str) -> Result<(), ()> {
    if !is_from_owner(&msg, tgsvc) {
        return Ok(());
    }

    let arg: Option<i64> = args
        .trim()
        .split_whitespace()
        .nth(0)
        .and_then(|arg| arg.parse().ok());
    let (maybe_leave_chat, resp) = match arg {
        Some(id) => (Some(tgsvc.api.send(LeaveChat::new(ChatId::from(id)))), "OK"),
        None => (None, "Invalid ID"),
    };

    let send_response = tgsvc.api.send(SendMessage::new(&msg.chat, resp));

    tokio::spawn(async move {
        if let Some(fut) = maybe_leave_chat {
            drop(fut.await.map_err(|e| warn!("failed to leave group: {}", e)));
        }
        drop(
            send_response
                .await
                .map_err(|e| warn!("failed to send message: {}", e)),
        );
    });
    Ok(())
}

impl TgSvc {
    async fn run() -> Result<(), ()> {
        let cfg = util::decode::<TgCfg, _>("evalbot.tg.toml")
            .await
            .map(|cfg| {
                debug!("Loaded config: {:?}", cfg);
                cfg
            })
            .map_err(|e| error!("failed to read evalbot.tg.toml: {}", e))?;
        let wl = util::decode::<TgWhitelist, _>(WHITELIST_FILENAME)
            .await
            .or_else(|e| {
                warn!("failed to read whitelist: {}; using empty whitelist", e);
                Ok(TgWhitelist {
                    priv_enabled: false,
                    group_enabled: false,
                    allowed: HashSet::new(),
                    blocked: HashSet::new(),
                })
            })?;

        let api = Api::new(&cfg.bot_id);
        let bot_user = api
            .send(GetMe)
            .await
            .map_err(|e| error!("failed to get bot's user info: {}", e))?;
        let service = EvalService::from_toml_file("evalbot.toml")
            .await
            .map_err(|e| error!("failed to read evalbot.toml: {}", e))?;
        TgSvc {
            api: Api::new(&cfg.bot_id),
            config: cfg,
            whitelist: RwLock::new(wl),
            service: service,
            username: bot_user
                .username
                .as_ref()
                .expect("Bot must have username")
                .clone(),
            bot_user: bot_user,
        }
        .handle()
        .await;
        Ok(())
    }

    async fn handle(self) {
        let me = Arc::new(self);

        let mut stream = me.api.stream();
        stream
            .timeout(Duration::from_secs(35))
            .error_delay(Duration::from_secs(30));
        while let Some(update) = stream.next().await {
            match update {
                Ok(update) => {
                    tokio::spawn(handle_update(update, me.clone()));
                }
                Err(error) => error!("received error: {:?}", error),
            }
        }
    }

    fn is_owner(&self, id: UserId) -> bool {
        self.config.owners.contains(&id.into())
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();
    drop(TgSvc::run().await);
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-wasm32-bare always compiled as panic=abort right now and this requires unwinding
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
// in a runtime panic.

#![feature(never_type)]

use std::{mem, panic};

struct Foo {
    x: u8,
    y: !,
}

enum Bar {}

fn main() {
    unsafe {
        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<!>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type ! using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<!>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type ! using mem::zeroed"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<Foo>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Foo using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<Foo>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Foo using mem::zeroed"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<Bar>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Bar using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<Bar>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Bar using mem::zeroed"
            })),
            Some(true)
        );
    }
}
<commit_msg>allow dead_code<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-wasm32-bare always compiled as panic=abort right now and this requires unwinding
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
// in a runtime panic.

#![feature(never_type)]

use std::{mem, panic};

#[allow(dead_code)]
struct Foo {
    x: u8,
    y: !,
}

enum Bar {}

fn main() {
    unsafe {
        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<!>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type ! using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<!>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type ! using mem::zeroed"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<Foo>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Foo using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<Foo>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Foo using mem::zeroed"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::uninitialized::<Bar>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Bar using mem::uninitialized"
            })),
            Some(true)
        );

        assert_eq!(
            panic::catch_unwind(|| {
                mem::zeroed::<Bar>()
            }).err().and_then(|a| a.downcast_ref::<String>().map(|s| {
                s == "Attempted to instantiate uninhabited type Bar using mem::zeroed"
            })),
            Some(true)
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use debug_interface::NodeDebugClient;
use diem_client::Client as JsonRpcClient;
use diem_config::{config::NodeConfig, network_id::NetworkId};
use diem_sdk::types::PeerId;
use std::collections::HashMap;
use url::Url;

#[derive(Debug)]
pub enum HealthCheckError {
    NotRunning,
    RpcFailure(anyhow::Error),
    Unknown(anyhow::Error),
}

impl std::fmt::Display for HealthCheckError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for HealthCheckError {}

/// Trait used to represent a running Validator or FullNode
pub trait Node {
    /// Return the PeerId of this Node
    fn peer_id(&self) -> PeerId;

    /// Return the human readable name of this Node
    fn name(&self) -> &str;

    /// Return the URL for the JSON-RPC endpoint of this Node
    fn json_rpc_endpoint(&self) -> Url;

    /// Return the URL for the debug-interface for this Node
    fn debug_endpoint(&self) -> Url;

    /// Return a reference to the Config this Node is using
    fn config(&self) -> &NodeConfig;

    /// Start this Node.
    /// This should be a noop if the Node is already running.
    fn start(&mut self) -> Result<()>;

    /// Stop this Node.
    /// This should be a noop if the Node isn't running.
    fn stop(&mut self) -> Result<()>;

    /// Restarts this Node by calling Node::Stop followed by Node::Start
    fn restart(&mut self) -> Result<()> {
        self.stop()?;
        self.start()
    }

    /// Clears this Node's Storage
    fn clear_storage(&mut self) -> Result<()>;

    /// Performs a Health Check on the Node
    fn health_check(&mut self) -> Result<(), HealthCheckError>;
}

/// Trait used to represent a running Validator
pub trait Validator: Node {
    fn check_connectivity(&self, expected_peers: usize) -> Result<bool> {
        if expected_peers == 0 {
            return Ok(true);
        }

        self.get_connected_peers(NetworkId::Validator, None)
            .map(|maybe_n| maybe_n.map(|n| n >= expected_peers as i64).unwrap_or(false))
    }
}

/// Trait used to represent a running FullNode
pub trait FullNode: Node {}

impl<T: ?Sized> NodeExt for T where T: Node {}

pub trait NodeExt: Node {
    /// Return JSON-RPC client of this Node
    fn json_rpc_client(&self) -> JsonRpcClient {
        JsonRpcClient::new(self.json_rpc_endpoint().to_string())
    }

    /// Return a NodeDebugClient for this Node
    fn debug_client(&self) -> NodeDebugClient {
        NodeDebugClient::from_url(self.debug_endpoint())
    }

    /// Restarts this Node by calling Node::Stop followed by Node::Start
    fn restart(&mut self) -> Result<()> {
        self.stop()?;
        self.start()
    }

    /// Query a Metric for from this Node
    fn get_metric(&self, metric_name: &str) -> Result<Option<i64>> {
        self.debug_client().get_node_metric(metric_name)
    }

    fn get_metric_with_fields(
        &self,
        metric_name: &str,
        fields: HashMap<String, String>,
    ) -> Result<Option<i64>> {
        let filtered: Vec<_> = self
            .debug_client()
            .get_node_metric_with_name(metric_name)?
            .into_iter()
            .flat_map(|map| map.into_iter())
            .filter_map(|(metric, metric_value)| {
                if fields
                    .iter()
                    .all(|(key, value)| metric.contains(&format!("{}={}", key, value)))
                {
                    Some(metric_value)
                } else {
                    None
                }
            })
            .collect();

        Ok(if filtered.is_empty() {
            None
        } else {
            Some(filtered.iter().sum())
        })
    }

    fn get_connected_peers(
        &self,
        network_id: NetworkId,
        direction: Option<&str>,
    ) -> Result<Option<i64>> {
        let mut map = HashMap::new();
        map.insert("network_id".to_string(), network_id.to_string());
        if let Some(direction) = direction {
            map.insert("direction".to_string(), direction.to_string());
        }
        self.get_metric_with_fields("diem_connections", map)
    }
}
<commit_msg>forge: add liveness_check method to NodeExt<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use debug_interface::NodeDebugClient;
use diem_client::Client as JsonRpcClient;
use diem_config::{config::NodeConfig, network_id::NetworkId};
use diem_sdk::types::PeerId;
use std::collections::HashMap;
use url::Url;

#[derive(Debug)]
pub enum HealthCheckError {
    NotRunning,
    RpcFailure(anyhow::Error),
    Unknown(anyhow::Error),
}

impl std::fmt::Display for HealthCheckError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for HealthCheckError {}

/// Trait used to represent a running Validator or FullNode
pub trait Node {
    /// Return the PeerId of this Node
    fn peer_id(&self) -> PeerId;

    /// Return the human readable name of this Node
    fn name(&self) -> &str;

    /// Return the URL for the JSON-RPC endpoint of this Node
    fn json_rpc_endpoint(&self) -> Url;

    /// Return the URL for the debug-interface for this Node
    fn debug_endpoint(&self) -> Url;

    /// Return a reference to the Config this Node is using
    fn config(&self) -> &NodeConfig;

    /// Start this Node.
    /// This should be a noop if the Node is already running.
    fn start(&mut self) -> Result<()>;

    /// Stop this Node.
    /// This should be a noop if the Node isn't running.
    fn stop(&mut self) -> Result<()>;

    /// Restarts this Node by calling Node::Stop followed by Node::Start
    fn restart(&mut self) -> Result<()> {
        self.stop()?;
        self.start()
    }

    /// Clears this Node's Storage
    fn clear_storage(&mut self) -> Result<()>;

    /// Performs a Health Check on the Node
    fn health_check(&mut self) -> Result<(), HealthCheckError>;
}

/// Trait used to represent a running Validator
pub trait Validator: Node {
    fn check_connectivity(&self, expected_peers: usize) -> Result<bool> {
        if expected_peers == 0 {
            return Ok(true);
        }

        self.get_connected_peers(NetworkId::Validator, None)
            .map(|maybe_n| maybe_n.map(|n| n >= expected_peers as i64).unwrap_or(false))
    }
}

/// Trait used to represent a running FullNode
pub trait FullNode: Node {}

impl<T: ?Sized> NodeExt for T where T: Node {}

pub trait NodeExt: Node {
    /// Return JSON-RPC client of this Node
    fn json_rpc_client(&self) -> JsonRpcClient {
        JsonRpcClient::new(self.json_rpc_endpoint().to_string())
    }

    /// Return a NodeDebugClient for this Node
    fn debug_client(&self) -> NodeDebugClient {
        NodeDebugClient::from_url(self.debug_endpoint())
    }

    /// Restarts this Node by calling Node::Stop followed by Node::Start
    fn restart(&mut self) -> Result<()> {
        self.stop()?;
        self.start()
    }

    /// Query a Metric for from this Node
    fn get_metric(&self, metric_name: &str) -> Result<Option<i64>> {
        self.debug_client().get_node_metric(metric_name)
    }

    fn get_metric_with_fields(
        &self,
        metric_name: &str,
        fields: HashMap<String, String>,
    ) -> Result<Option<i64>> {
        let filtered: Vec<_> = self
            .debug_client()
            .get_node_metric_with_name(metric_name)?
            .into_iter()
            .flat_map(|map| map.into_iter())
            .filter_map(|(metric, metric_value)| {
                if fields
                    .iter()
                    .all(|(key, value)| metric.contains(&format!("{}={}", key, value)))
                {
                    Some(metric_value)
                } else {
                    None
                }
            })
            .collect();

        Ok(if filtered.is_empty() {
            None
        } else {
            Some(filtered.iter().sum())
        })
    }

    fn get_connected_peers(
        &self,
        network_id: NetworkId,
        direction: Option<&str>,
    ) -> Result<Option<i64>> {
        let mut map = HashMap::new();
        map.insert("network_id".to_string(), network_id.to_string());
        if let Some(direction) = direction {
            map.insert("direction".to_string(), direction.to_string());
        }
        self.get_metric_with_fields("diem_connections", map)
    }

    fn liveness_check(&self, seconds: u64) -> Result<()> {
        let mut url = self.json_rpc_endpoint();
        url.set_path("-/healthy");
        url.set_query(Some(&format!("duration_secs={}", seconds)));

        let resp = reqwest::blocking::Client::new().get(url).send()?;

        if !resp.status().is_success() {
            return Err(anyhow::anyhow!(
                "Node {} failed a liveness check",
                self.name()
            ));
        }

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


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(target_os = "nacl", allow(dead_code))]

use env;
use io::prelude::*;
use io;
use libc;
use str;
use sync::atomic::{self, Ordering};

pub use sys::backtrace::write;

#[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18;

#[cfg(target_pointer_width = "32")]
pub const HEX_WIDTH: usize = 10;

// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
    static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
    match ENABLED.load(Ordering::SeqCst) {
        1 => return false,
        2 => return true,
        _ => {}
    }

    let val = match env::var_os("RUST_BACKTRACE") {
        Some(x) => if &x == "0" { 1 } else { 2 },
        None => 1,
    };
    ENABLED.store(val, Ordering::SeqCst);
    val == 2
}

// These output functions should now be used everywhere to ensure consistency.
pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
              s: Option<&[u8]>) -> io::Result<()> {
    write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
    match s.and_then(|s| str::from_utf8(s).ok()) {
        Some(string) => demangle(w, string)?,
        None => write!(w, "<unknown>")?,
    }
    w.write_all(&['\n' as u8])
}

#[allow(dead_code)]
pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
                       more: bool) -> io::Result<()> {
    let file = str::from_utf8(file).unwrap_or("<unknown>");
    // prior line: "  ##: {:2$} - func"
    write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
    if more {
        write!(w, " <... and possibly more>")?;
    }
    w.write_all(&['\n' as u8])
}

// All rust symbols are in theory lists of "::"-separated identifiers. Some
// assemblers, however, can't handle these characters in symbol names. To get
// around this, we use C++-style mangling. The mangling method is:
//
// 1. Prefix the symbol with "_ZN"
// 2. For each element of the path, emit the length plus the element
// 3. End the path with "E"
//
// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
//
// We're the ones printing our backtraces, so we can't rely on anything else to
// demangle our symbols. It's *much* nicer to look at demangled symbols, so
// this function is implemented to give us nice pretty output.
//
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
    // First validate the symbol. If it doesn't look like anything we're
    // expecting, we just print it literally. Note that we must handle non-rust
    // symbols because we could have any function in the backtrace.
    let mut valid = true;
    let mut inner = s;
    if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
        inner = &s[3 .. s.len() - 1];
    // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
    } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") {
        inner = &s[2 .. s.len() - 1];
    } else {
        valid = false;
    }

    if valid {
        let mut chars = inner.chars();
        while valid {
            let mut i = 0;
            for c in chars.by_ref() {
                if c.is_numeric() {
                    i = i * 10 + c as usize - '0' as usize;
                } else {
                    break
                }
            }
            if i == 0 {
                valid = chars.next().is_none();
                break
            } else if chars.by_ref().take(i - 1).count() != i - 1 {
                valid = false;
            }
        }
    }

    // Alright, let's do this.
    if !valid {
        writer.write_all(s.as_bytes())?;
    } else {
        let mut first = true;
        while !inner.is_empty() {
            if !first {
                writer.write_all(b"::")?;
            } else {
                first = false;
            }
            let mut rest = inner;
            while rest.chars().next().unwrap().is_numeric() {
                rest = &rest[1..];
            }
            let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
            inner = &rest[i..];
            rest = &rest[..i];
            while !rest.is_empty() {
                if rest.starts_with("$") {
                    macro_rules! demangle {
                        ($($pat:expr, => $demangled:expr),*) => ({
                            $(if rest.starts_with($pat) {
                                try!(writer.write_all($demangled));
                                rest = &rest[$pat.len()..];
                              } else)*
                            {
                                try!(writer.write_all(rest.as_bytes()));
                                break;
                            }

                        })
                    }

                    // see src/librustc/back/link.rs for these mappings
                    demangle! (
                        "$SP$", => b"@",
                        "$BP$", => b"*",
                        "$RF$", => b"&",
                        "$LT$", => b"<",
                        "$GT$", => b">",
                        "$LP$", => b"(",
                        "$RP$", => b")",
                        "$C$", => b",",

                        // in theory we can demangle any Unicode code point, but
                        // for simplicity we just catch the common ones.
                        "$u7e$", => b"~",
                        "$u20$", => b" ",
                        "$u27$", => b"'",
                        "$u5b$", => b"[",
                        "$u5d$", => b"]"
                    )
                } else {
                    let idx = match rest.find('$') {
                        None => rest.len(),
                        Some(i) => i,
                    };
                    writer.write_all(rest[..idx].as_bytes())?;
                    rest = &rest[idx..];
                }
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use prelude::v1::*;
    use sys_common;
    macro_rules! t { ($a:expr, $b:expr) => ({
        let mut m = Vec::new();
        sys_common::backtrace::demangle(&mut m, $a).unwrap();
        assert_eq!(String::from_utf8(m).unwrap(), $b);
    }) }

    #[test]
    fn demangle() {
        t!("test", "test");
        t!("_ZN4testE", "test");
        t!("_ZN4test", "_ZN4test");
        t!("_ZN4test1a2bcE", "test::a::bc");
    }

    #[test]
    fn demangle_dollars() {
        t!("_ZN4$RP$E", ")");
        t!("_ZN8$RF$testE", "&test");
        t!("_ZN8$BP$test4foobE", "*test::foob");
        t!("_ZN9$u20$test4foobE", " test::foob");
    }

    #[test]
    fn demangle_many_dollars() {
        t!("_ZN13test$u20$test4foobE", "test test::foob");
        t!("_ZN12test$BP$test4foobE", "test*test::foob");
    }

    #[test]
    fn demangle_windows() {
        t!("ZN4testE", "test");
        t!("ZN13test$u20$test4foobE", "test test::foob");
        t!("ZN12test$RF$test4foobE", "test&test::foob");
    }
}
<commit_msg>Auto merge of #34753 - sfackler:demangle-curly-braces, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(target_os = "nacl", allow(dead_code))]

use env;
use io::prelude::*;
use io;
use libc;
use str;
use sync::atomic::{self, Ordering};

pub use sys::backtrace::write;

#[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18;

#[cfg(target_pointer_width = "32")]
pub const HEX_WIDTH: usize = 10;

// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
    static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
    match ENABLED.load(Ordering::SeqCst) {
        1 => return false,
        2 => return true,
        _ => {}
    }

    let val = match env::var_os("RUST_BACKTRACE") {
        Some(x) => if &x == "0" { 1 } else { 2 },
        None => 1,
    };
    ENABLED.store(val, Ordering::SeqCst);
    val == 2
}

// These output functions should now be used everywhere to ensure consistency.
pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
              s: Option<&[u8]>) -> io::Result<()> {
    write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
    match s.and_then(|s| str::from_utf8(s).ok()) {
        Some(string) => demangle(w, string)?,
        None => write!(w, "<unknown>")?,
    }
    w.write_all(&['\n' as u8])
}

#[allow(dead_code)]
pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
                       more: bool) -> io::Result<()> {
    let file = str::from_utf8(file).unwrap_or("<unknown>");
    // prior line: "  ##: {:2$} - func"
    write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
    if more {
        write!(w, " <... and possibly more>")?;
    }
    w.write_all(&['\n' as u8])
}

// All rust symbols are in theory lists of "::"-separated identifiers. Some
// assemblers, however, can't handle these characters in symbol names. To get
// around this, we use C++-style mangling. The mangling method is:
//
// 1. Prefix the symbol with "_ZN"
// 2. For each element of the path, emit the length plus the element
// 3. End the path with "E"
//
// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
//
// We're the ones printing our backtraces, so we can't rely on anything else to
// demangle our symbols. It's *much* nicer to look at demangled symbols, so
// this function is implemented to give us nice pretty output.
//
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
    // First validate the symbol. If it doesn't look like anything we're
    // expecting, we just print it literally. Note that we must handle non-rust
    // symbols because we could have any function in the backtrace.
    let mut valid = true;
    let mut inner = s;
    if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
        inner = &s[3 .. s.len() - 1];
    // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
    } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") {
        inner = &s[2 .. s.len() - 1];
    } else {
        valid = false;
    }

    if valid {
        let mut chars = inner.chars();
        while valid {
            let mut i = 0;
            for c in chars.by_ref() {
                if c.is_numeric() {
                    i = i * 10 + c as usize - '0' as usize;
                } else {
                    break
                }
            }
            if i == 0 {
                valid = chars.next().is_none();
                break
            } else if chars.by_ref().take(i - 1).count() != i - 1 {
                valid = false;
            }
        }
    }

    // Alright, let's do this.
    if !valid {
        writer.write_all(s.as_bytes())?;
    } else {
        let mut first = true;
        while !inner.is_empty() {
            if !first {
                writer.write_all(b"::")?;
            } else {
                first = false;
            }
            let mut rest = inner;
            while rest.chars().next().unwrap().is_numeric() {
                rest = &rest[1..];
            }
            let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
            inner = &rest[i..];
            rest = &rest[..i];
            while !rest.is_empty() {
                if rest.starts_with("$") {
                    macro_rules! demangle {
                        ($($pat:expr, => $demangled:expr),*) => ({
                            $(if rest.starts_with($pat) {
                                try!(writer.write_all($demangled));
                                rest = &rest[$pat.len()..];
                              } else)*
                            {
                                try!(writer.write_all(rest.as_bytes()));
                                break;
                            }

                        })
                    }

                    // see src/librustc/back/link.rs for these mappings
                    demangle! (
                        "$SP$", => b"@",
                        "$BP$", => b"*",
                        "$RF$", => b"&",
                        "$LT$", => b"<",
                        "$GT$", => b">",
                        "$LP$", => b"(",
                        "$RP$", => b")",
                        "$C$", => b",",

                        // in theory we can demangle any Unicode code point, but
                        // for simplicity we just catch the common ones.
                        "$u7e$", => b"~",
                        "$u20$", => b" ",
                        "$u27$", => b"'",
                        "$u5b$", => b"[",
                        "$u5d$", => b"]",
                        "$u7b$", => b"{",
                        "$u7d$", => b"}"
                    )
                } else {
                    let idx = match rest.find('$') {
                        None => rest.len(),
                        Some(i) => i,
                    };
                    writer.write_all(rest[..idx].as_bytes())?;
                    rest = &rest[idx..];
                }
            }
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use prelude::v1::*;
    use sys_common;
    macro_rules! t { ($a:expr, $b:expr) => ({
        let mut m = Vec::new();
        sys_common::backtrace::demangle(&mut m, $a).unwrap();
        assert_eq!(String::from_utf8(m).unwrap(), $b);
    }) }

    #[test]
    fn demangle() {
        t!("test", "test");
        t!("_ZN4testE", "test");
        t!("_ZN4test", "_ZN4test");
        t!("_ZN4test1a2bcE", "test::a::bc");
    }

    #[test]
    fn demangle_dollars() {
        t!("_ZN4$RP$E", ")");
        t!("_ZN8$RF$testE", "&test");
        t!("_ZN8$BP$test4foobE", "*test::foob");
        t!("_ZN9$u20$test4foobE", " test::foob");
    }

    #[test]
    fn demangle_many_dollars() {
        t!("_ZN13test$u20$test4foobE", "test test::foob");
        t!("_ZN12test$BP$test4foobE", "test*test::foob");
    }

    #[test]
    fn demangle_windows() {
        t!("ZN4testE", "test");
        t!("ZN13test$u20$test4foobE", "test test::foob");
        t!("ZN12test$RF$test4foobE", "test&test::foob");
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]

// Below, these constants are defined as enum variants that by itself would
// have a lower alignment than the enum type. Ensure that we mark them
// correctly with the higher alignment of the enum.

// CHECK: @STATIC = {{.*}}, align 4

// This checks the constants from inline_enum_const
// CHECK: @ref{{[0-9]+}} = {{.*}}, align 2

// This checks the constants from {low,high}_align_const, they share the same
// constant, but the alignment differs, so the higher one should be used
// CHECK: [[LOW_HIGH:@ref[0-9]+]] = {{.*}}, align 4
// CHECK: [[LOW_HIGH_REF:@const[0-9]+]] = {{.*}} [[LOW_HIGH]]

#[derive(Copy, Clone)]

// repr(i16) is required for the {low,high}_align_const test
#[repr(i16)]
pub enum E<A, B> {
    A(A),
    B(B),
}

#[no_mangle]
pub static STATIC: E<i16, i32> = E::A(0);

// CHECK-LABEL: @static_enum_const
#[no_mangle]
pub fn static_enum_const() -> E<i16, i32> {
   STATIC
}

// CHECK-LABEL: @inline_enum_const
#[no_mangle]
pub fn inline_enum_const() -> E<i8, i16> {
    *&E::A(0)
}

// CHECK-LABEL: @low_align_const
#[no_mangle]
pub fn low_align_const() -> E<i16, [i16; 3]> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]]
    *&E::A(0)
}

// CHECK-LABEL: @high_align_const
#[no_mangle]
pub fn high_align_const() -> E<i16, i32> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]]
    *&E::A(0)
}
<commit_msg>Adapt codegen test to new naming scheme for generated symbols.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]

// Below, these constants are defined as enum variants that by itself would
// have a lower alignment than the enum type. Ensure that we mark them
// correctly with the higher alignment of the enum.

// CHECK: @STATIC = {{.*}}, align 4

// This checks the constants from inline_enum_const
// CHECK: @ref.{{[0-9]+}} = {{.*}}, align 2

// This checks the constants from {low,high}_align_const, they share the same
// constant, but the alignment differs, so the higher one should be used
// CHECK: [[LOW_HIGH:@ref.[0-9]+]] = {{.*}}, align 4
// CHECK: [[LOW_HIGH_REF:@const.[0-9]+]] = {{.*}} [[LOW_HIGH]]

#[derive(Copy, Clone)]

// repr(i16) is required for the {low,high}_align_const test
#[repr(i16)]
pub enum E<A, B> {
    A(A),
    B(B),
}

#[no_mangle]
pub static STATIC: E<i16, i32> = E::A(0);

// CHECK-LABEL: @static_enum_const
#[no_mangle]
pub fn static_enum_const() -> E<i16, i32> {
   STATIC
}

// CHECK-LABEL: @inline_enum_const
#[no_mangle]
pub fn inline_enum_const() -> E<i8, i16> {
    *&E::A(0)
}

// CHECK-LABEL: @low_align_const
#[no_mangle]
pub fn low_align_const() -> E<i16, [i16; 3]> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]]
    *&E::A(0)
}

// CHECK-LABEL: @high_align_const
#[no_mangle]
pub fn high_align_const() -> E<i16, i32> {
// Check that low_align_const and high_align_const use the same constant
// CHECK: load {{.*}} bitcast ({ i16, i16, [4 x i8] }** [[LOW_HIGH_REF]]
    *&E::A(0)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tidy check to ensure that there are no binaries checked into the source tree
//! by accident.
//!
//! In the past we've accidentally checked in test binaries and such which add a
//! huge amount of bloat to the git history, so it's good to just ensure we
//! don't do that again :)

use std::path::Path;

// All files are executable on Windows, so just check on Unix
#[cfg(windows)]
pub fn check(_path: &Path, _bad: &mut bool) {}

#[cfg(unix)]
pub fn check(path: &Path, bad: &mut bool) {
    use std::fs;
    use std::process::{Command, Stdio};
    use std::os::unix::prelude::*;

    super::walk(path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/etc"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".py", ".sh"];
        if extensions.iter().any(|e| filename.ends_with(e)) {
            return
        }

        let metadata = t!(fs::symlink_metadata(&file), &file);
        if metadata.mode() & 0o111 != 0 {
            let rel_path = file.strip_prefix(path).unwrap();
            let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
            let ret_code = Command::new("git")
                                        .arg("ls-files")
                                        .arg(&git_friendly_path)
                                        .current_dir(path)
                                        .stdout(Stdio::null())
                                        .stderr(Stdio::null())
                                        .status()
                                        .unwrap_or_else(|e| {
                                            panic!("could not run git ls-files: {}", e);
                                        });
            if ret_code.success() {
                println!("binary checked into source: {}", file.display());
                *bad = true;
            }
        }
    })
}

<commit_msg>Skip binary tidy check when on Windows Linux Subsystem<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tidy check to ensure that there are no binaries checked into the source tree
//! by accident.
//!
//! In the past we've accidentally checked in test binaries and such which add a
//! huge amount of bloat to the git history, so it's good to just ensure we
//! don't do that again :)

use std::path::Path;

// All files are executable on Windows, so just check on Unix
#[cfg(windows)]
pub fn check(_path: &Path, _bad: &mut bool) {}

#[cfg(unix)]
pub fn check(path: &Path, bad: &mut bool) {
    use std::fs;
    use std::io::Read;
    use std::process::{Command, Stdio};
    use std::os::unix::prelude::*;

    if let Ok(mut file) = fs::File::open("/proc/version") {
        let mut contents = String::new();
        file.read_to_string(&mut contents).unwrap();
        // Probably on Windows Linux Subsystem, all files will be marked as
        // executable, so skip checking.
        if contents.contains("Microsoft") {
            return;
        }
    }

    super::walk(path,
                &mut |path| super::filter_dirs(path) || path.ends_with("src/etc"),
                &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".py", ".sh"];
        if extensions.iter().any(|e| filename.ends_with(e)) {
            return
        }

        let metadata = t!(fs::symlink_metadata(&file), &file);
        if metadata.mode() & 0o111 != 0 {
            let rel_path = file.strip_prefix(path).unwrap();
            let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
            let ret_code = Command::new("git")
                                        .arg("ls-files")
                                        .arg(&git_friendly_path)
                                        .current_dir(path)
                                        .stdout(Stdio::null())
                                        .stderr(Stdio::null())
                                        .status()
                                        .unwrap_or_else(|e| {
                                            panic!("could not run git ls-files: {}", e);
                                        });
            if ret_code.success() {
                println!("binary checked into source: {}", file.display());
                *bad = true;
            }
        }
    })
}

<|endoftext|>


<commit_before>#![no_main]
extern crate tests;

#[macro_use]
extern crate libfuzzer_sys;

fuzz_target!(|data: &[u8]| {
    tests::tree::fuzz_then_shrink(data);
});
<commit_msg>Delete fuzz_tree_ops.rs<commit_after><|endoftext|>


<commit_before>use std::collections::HashMap;
use std::cmp::min;

use super::crystallized_state::CrystallizedState;
use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::config::Config;

// Returns the maximum possible shards for a given validator_count
// and configuration.
pub fn get_crosslink_shards_count(
    active_validator_count: &usize,
    config: &Config)
    -> u16
{
    let system_shard_count: u16 = config.shard_count;
    let notaries_per_crosslink: u16 = config.notaries_per_crosslink;

    assert!(notaries_per_crosslink > 0, "Shards must require > 0 notaries.");

    let notarisable_shard_count =
        *active_validator_count as u64 / notaries_per_crosslink as u64;

    min(notarisable_shard_count, system_shard_count as u64) as u16
}

pub fn get_crosslink_shards(
    cry_state: &CrystallizedState,
    config: &Config)
    -> Vec<u16>
{
    let max_shard_count: u16 = config.shard_count;
    let first_shard: u16 = cry_state.next_shard;
    assert!(first_shard < max_shard_count, "CrystallizedState.next_shard \
    must be less than Config.shard_count.");

    let shard_count = get_crosslink_shards_count(
        &cry_state.num_active_validators(),
        &config);

    let unwrapped_shards: u16 = min(first_shard + shard_count, max_shard_count);
    let wrapped_shards: u16 = (first_shard + shard_count) % max_shard_count;
    let mut crosslink_shards: Vec<u16> = (first_shard..unwrapped_shards).collect();
    crosslink_shards.append(&mut (0_u16..wrapped_shards).collect());
    crosslink_shards
}

/* Work in progres...
pub fn process_crosslinks(
    cry_state: &CrystallizedState,
    partial_crosslinks: &Vec<PartialCrosslinkRecord>,
    config: &Config)
    -> (Vec<i64>, Vec<PartialCrosslinkRecord>)
{
    assert!(partial_crosslinks.len() > 0, "No crosslinks present.");

    let mut map: HashMap<u16, (&PartialCrosslinkRecord, u64)> = HashMap::new();

    for pc in partial_crosslinks {
        let vote_count = pc.voter_bitfield.num_true_bits();
        let mut competiting_vote_count = 0;
        match map.get(&pc.shard_id) {
            Some(&competitor) => competiting_vote_count = competitor.1,
            None => {}
        }
        // Here we implicitly avoid adding crosslinks with 0 votes
        // to our map.
        if vote_count > competiting_vote_count {
            map.insert(pc.shard_id, (pc, vote_count));
        }
    }

    let mut new_partial_crosslinks: Vec<&PartialCrosslinkRecord> = Vec::new();
    map.iter_mut()
        .for_each(|(_, v)| new_partial_crosslinks.push(v.0));

    // To be completed...

    (Vec::new(), Vec::new())
}
*/

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

    #[test]
    fn test_crosslink_shard_count_with_varying_active_vals() {
        let mut config = Config::standard();

        config.shard_count = 10;
        config.notaries_per_crosslink = 10;

        let mut c = get_crosslink_shards_count(
            &100,
            &config);
        assert_eq!(c, 10);

        c = get_crosslink_shards_count(
            &101,
            &config);
        assert_eq!(c, 10);

        c = get_crosslink_shards_count(
            &99,
            &config);
        assert_eq!(c, 9);

        c = get_crosslink_shards_count(
            &0,
            &config);
        assert_eq!(c, 0);
    }

    #[test]
    #[should_panic(expected = "must require > 0 notaries.")]
    fn test_crosslink_shard_count_with_zero_notaries_per_crosslink() {
        let mut config = Config::standard();

        config.shard_count = 10;
        config.notaries_per_crosslink = 0;

        let validators: u16 = 10;

        let c = get_crosslink_shards_count(
            &(validators as usize),
            &config);
    }

    #[test]
    fn test_crosslink_shard_getter_with_5_shards() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();

        config.shard_count = 5;
        config.notaries_per_crosslink = 2;

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        cry_state.next_shard = 0;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [0, 1, 2, 3, 4]);

        cry_state.next_shard = 4;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [4, 0, 1, 2, 3]);

        cry_state.next_shard = 1;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [1, 2, 3, 4, 0]);

        cry_state.next_shard = 3;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [3, 4, 0, 1, 2]);
    }

    #[test]
    #[should_panic(expected = "next_shard must be less than Config.shard_count")]
    fn test_crosslink_shard_getter_with_too_large_next_shard() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();

        config.shard_count = 1;
        config.notaries_per_crosslink = 2;

        (0..2).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        cry_state.next_shard = 6;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
    }
}
<commit_msg>Fix warnings warnings<commit_after>use std::collections::HashMap;
use std::cmp::min;

use super::crystallized_state::CrystallizedState;
use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::config::Config;

// Returns the maximum possible shards for a given validator_count
// and configuration.
pub fn get_crosslink_shards_count(
    active_validator_count: &usize,
    config: &Config)
    -> u16
{
    let system_shard_count: u16 = config.shard_count;
    let notaries_per_crosslink: u16 = config.notaries_per_crosslink;

    assert!(notaries_per_crosslink > 0, "Shards must require > 0 notaries.");

    let notarisable_shard_count =
        *active_validator_count as u64 / notaries_per_crosslink as u64;

    min(notarisable_shard_count, system_shard_count as u64) as u16
}

pub fn get_crosslink_shards(
    cry_state: &CrystallizedState,
    config: &Config)
    -> Vec<u16>
{
    let max_shard_count: u16 = config.shard_count;
    let first_shard: u16 = cry_state.next_shard;
    assert!(first_shard < max_shard_count, "CrystallizedState.next_shard \
    must be less than Config.shard_count.");

    let shard_count = get_crosslink_shards_count(
        &cry_state.num_active_validators(),
        &config);

    let unwrapped_shards: u16 = min(first_shard + shard_count, max_shard_count);
    let wrapped_shards: u16 = (first_shard + shard_count) % max_shard_count;
    let mut crosslink_shards: Vec<u16> = (first_shard..unwrapped_shards).collect();
    crosslink_shards.append(&mut (0_u16..wrapped_shards).collect());
    crosslink_shards
}

/* Work in progres...
pub fn process_crosslinks(
    cry_state: &CrystallizedState,
    partial_crosslinks: &Vec<PartialCrosslinkRecord>,
    config: &Config)
    -> (Vec<i64>, Vec<PartialCrosslinkRecord>)
{
    assert!(partial_crosslinks.len() > 0, "No crosslinks present.");

    let mut map: HashMap<u16, (&PartialCrosslinkRecord, u64)> = HashMap::new();

    for pc in partial_crosslinks {
        let vote_count = pc.voter_bitfield.num_true_bits();
        let mut competiting_vote_count = 0;
        match map.get(&pc.shard_id) {
            Some(&competitor) => competiting_vote_count = competitor.1,
            None => {}
        }
        // Here we implicitly avoid adding crosslinks with 0 votes
        // to our map.
        if vote_count > competiting_vote_count {
            map.insert(pc.shard_id, (pc, vote_count));
        }
    }

    let mut new_partial_crosslinks: Vec<&PartialCrosslinkRecord> = Vec::new();
    map.iter_mut()
        .for_each(|(_, v)| new_partial_crosslinks.push(v.0));

    // To be completed...

    (Vec::new(), Vec::new())
}
*/

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

    #[test]
    fn test_crosslink_shard_count_with_varying_active_vals() {
        let mut config = Config::standard();

        config.shard_count = 10;
        config.notaries_per_crosslink = 10;

        let mut c = get_crosslink_shards_count(
            &100,
            &config);
        assert_eq!(c, 10);

        c = get_crosslink_shards_count(
            &101,
            &config);
        assert_eq!(c, 10);

        c = get_crosslink_shards_count(
            &99,
            &config);
        assert_eq!(c, 9);

        c = get_crosslink_shards_count(
            &0,
            &config);
        assert_eq!(c, 0);
    }

    #[test]
    #[should_panic(expected = "must require > 0 notaries.")]
    fn test_crosslink_shard_count_with_zero_notaries_per_crosslink() {
        let mut config = Config::standard();

        config.shard_count = 10;
        config.notaries_per_crosslink = 0;

        let validators: u16 = 10;

        let _ = get_crosslink_shards_count(
            &(validators as usize),
            &config);
    }

    #[test]
    fn test_crosslink_shard_getter_with_5_shards() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();

        config.shard_count = 5;
        config.notaries_per_crosslink = 2;

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        cry_state.next_shard = 0;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [0, 1, 2, 3, 4]);

        cry_state.next_shard = 4;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [4, 0, 1, 2, 3]);

        cry_state.next_shard = 1;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [1, 2, 3, 4, 0]);

        cry_state.next_shard = 3;
        let c = get_crosslink_shards(
            &cry_state,
            &config);
        assert_eq!(c, [3, 4, 0, 1, 2]);
    }

    #[test]
    #[should_panic(expected = "next_shard must be less than Config.shard_count")]
    fn test_crosslink_shard_getter_with_too_large_next_shard() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();

        config.shard_count = 1;
        config.notaries_per_crosslink = 2;

        (0..2).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        cry_state.next_shard = 6;
        let _ = get_crosslink_shards(
            &cry_state,
            &config);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use specs::{Entities, Read, ReadStorage, System, WriteStorage};

use crate::animation::ImageType;
use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::{Action, Body, Draw, Facing, Position, State, Weapon};
use crate::files::collide::CollisionBoxes;
use crate::game::EncounterTextures;
use crate::objects::TextureAtlas;
use crate::rect::{Point, Rect};

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        Read<'a, CollisionBoxes>,
        Read<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, State>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            state,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;
        for (draw, position, state, body, weapon) in
            (&draw, &position, &state, &mut body, &mut weapon).join()
        {
            let mut weapon_boxes: Vec<Points> = vec![];
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter() {
                match image.image_type {
                    ImageType::Collider => {
                        if let Some(collision_points) = collision_data.get(&image.sheet) {
                            if let Some(points) = &collision_points.points[image.image] {
                                let mut image_global_x: i32 =
                                    position.x as i32 + image.x * state.direction as i32;
                                let image_global_y: i32 = position.y as i32 + image.y;
                                image_global_x = match state.direction {
                                    Facing::Left => image_global_x - points.max_x as i32,
                                    Facing::Right => image_global_x,
                                };
                                weapon_boxes.push(Points {
                                    bounding: Rect {
                                        // x: image.x * state.direction as i32,
                                        // y: image.y,
                                        x: image_global_x,
                                        y: image_global_y,
                                        w: points.max_x,
                                        h: points.max_y,
                                    },
                                    points: points
                                        .data
                                        .iter()
                                        .map(|p| Point {
                                            x: image_global_x + p.x * state.direction as i32,
                                            y: image_global_y + p.y,
                                            // x: p.x * state.direction as i32,
                                            // y: p.y,
                                        })
                                        .collect(),
                                })
                            }
                        }
                    }
                    ImageType::Collidee => {
                        if let Some(texture) = textures.get(&image.sheet) {
                            let rect = &texture.rects[image.image];
                            let mut image_global_x: i32 =
                                position.x as i32 + image.x * state.direction as i32;
                            image_global_x = match state.direction {
                                Facing::Left => image_global_x - rect.w as i32,
                                Facing::Right => image_global_x,
                            };
                            body_boxes.push(CollisionBox {
                                rect: Rect {
                                    x: image_global_x,
                                    y: position.y as i32 + image.y,
                                    w: rect.w,
                                    h: rect.h,
                                },
                                sheet: image.sheet.clone(),
                                image_num: image.image as u32,
                            })
                        }
                    }
                    _ => (),
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                body.collision_boxes = Some(body_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        Read<'a, EncounterTextures>,
        ReadStorage<'a, State>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, state, position, bodies, weapons, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        for (state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
            .join()
            .filter(|(s, ..)| s.action.is_attack())
        {
            for (defender, body, defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = check_collision(&textures, weapon, body);
                if hit {
                    println!("hit");
                }
            }
        }
    }
}

fn check_collision(textures: &HashMap<String, TextureAtlas>, weapon: &Weapon, body: &Body) -> bool {
    for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
        // each attacking image in the attacker
        for body_part in body
            .collision_boxes
            .iter() // Option.iter()
            .flat_map(|v| v)
            .filter(|b| weapon_part.bounding.intersects(&b.rect))
        {
            // check that against each defending image in the defender
            for point in weapon_part
                .points
                .iter()
                .filter(|p| body_part.rect.contains_point(p))
            {
                // check the points collide and the pixel in the image is a part of the target
                let hit_x = point.x - body_part.rect.x;
                let hit_y = point.y - body_part.rect.y;
                let texture = textures
                    .get(&body_part.sheet)
                    .expect("Encounter hasn't loaded correct textures to as world resource");
                let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                let pixel = texture.image.pixels[collision_lookup];
                if pixel > 0 {
                    return true;
                }
            }
        }
    }
    false
}
<commit_msg>fix right facing pixel collisions.<commit_after>use std::collections::HashMap;

use specs::{Entities, Read, ReadStorage, System, WriteStorage};

use crate::animation::ImageType;
use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::{Action, Body, Draw, Facing, Position, State, Weapon};
use crate::files::collide::CollisionBoxes;
use crate::game::EncounterTextures;
use crate::objects::TextureAtlas;
use crate::rect::{Point, Rect};

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        Read<'a, CollisionBoxes>,
        Read<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, State>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            state,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;
        for (draw, position, state, body, weapon) in
            (&draw, &position, &state, &mut body, &mut weapon).join()
        {
            let mut weapon_boxes: Vec<Points> = vec![];
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter() {
                match image.image_type {
                    ImageType::Collider => {
                        if let Some(Some(points)) = &collision_data
                            .get(&image.sheet)
                            .and_then(|v| v.points.get(image.image))
                        {
                            let direction = state.direction as i32;
                            let image_global_x: i32 = position.x as i32 + image.x * direction;
                            let image_global_y: i32 = position.y as i32 + image.y;
                            let rect_x = match state.direction {
                                Facing::Left => image_global_x - points.max_x as i32,
                                Facing::Right => image_global_x,
                            };
                            weapon_boxes.push(Points {
                                bounding: Rect {
                                    x: rect_x,
                                    y: image_global_y,
                                    w: points.max_x,
                                    h: points.max_y,
                                },
                                points: points
                                    .data
                                    .iter()
                                    .map(|p| Point {
                                        x: image_global_x + p.x * direction,
                                        y: image_global_y + p.y,
                                    })
                                    .collect(),
                            })
                        }
                    }
                    ImageType::Collidee => {
                        if let Some(texture) = textures.get(&image.sheet) {
                            let rect = &texture.rects[image.image];
                            let mut image_global_x: i32 =
                                position.x as i32 + image.x * state.direction as i32;
                            image_global_x = match state.direction {
                                Facing::Left => image_global_x - rect.w as i32,
                                Facing::Right => image_global_x,
                            };
                            body_boxes.push(CollisionBox {
                                rect: Rect {
                                    x: image_global_x,
                                    y: position.y as i32 + image.y,
                                    w: rect.w,
                                    h: rect.h,
                                },
                                sheet: image.sheet.clone(),
                                image_num: image.image as u32,
                            })
                        }
                    }
                    _ => (),
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                body.collision_boxes = Some(body_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        Read<'a, EncounterTextures>,
        ReadStorage<'a, State>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, state, position, bodies, weapons, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        for (state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
            .join()
            .filter(|(s, ..)| s.action.is_attack())
        {
            for (defender, body, defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = check_collision(&textures, weapon, body);
                if hit {
                    println!("hit");
                }
            }
        }
    }
}

fn check_collision(textures: &HashMap<String, TextureAtlas>, weapon: &Weapon, body: &Body) -> bool {
    for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
        // each attacking image in the attacker
        for body_part in body
            .collision_boxes
            .iter() // Option.iter()
            .flat_map(|v| v)
            .filter(|b| weapon_part.bounding.intersects(&b.rect))
        {
            // check that against each defending image in the defender
            for point in weapon_part
                .points
                .iter()
                .filter(|p| body_part.rect.contains_point(p))
            {
                // check the points collide and the pixel in the image is a part of the target
                let hit_x = point.x - body_part.rect.x;
                let hit_y = point.y - body_part.rect.y;
                let texture = textures
                    .get(&body_part.sheet)
                    .expect("Encounter hasn't loaded correct textures to as world resource");
                let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                let pixel = texture.image.pixels[collision_lookup];
                if pixel > 0 {
                    return true;
                }
            }
        }
    }
    false
}
<|endoftext|>


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

use std::io::BufRead;

use error::{Error, Result};

pub struct Plan {
    pub name: String,
    pub version: String,
}

impl Plan {
    pub fn new(name: String, version: String) -> Self {
        Plan {
            name: name,
            version: version,
        }
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        let mut name: Option<String> = None;
        let mut version: Option<String> = None;
        for line in bytes.lines() {
            if let Ok(line) = line {
                let parts: Vec<&str> = line.splitn(2, "=").collect();
                match parts[0] {
                    "pkg_name" => name = Some(parts[1].to_string()),
                    "pkg_version" => version = Some(parts[1].to_string()),
                    _ => (),
                }
            }
        }
        if name.is_none() || version.is_none() {
            return Err(Error::PlanMalformed);
        }
        let plan = Plan::new(name.unwrap(), version.unwrap());
        Ok(plan)
    }
}
<commit_msg>Relax requirement for version in plan deserialize<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io::BufRead;

use error::{Error, Result};

pub struct Plan {
    pub name: String,
    pub version: String,
}

impl Plan {
    pub fn new(name: String, version: String) -> Self {
        Plan {
            name: name,
            version: version,
        }
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
        let mut name: Option<String> = None;
        let mut version: Option<String> = None;
        for line in bytes.lines() {
            if let Ok(line) = line {
                let parts: Vec<&str> = line.splitn(2, "=").collect();
                match parts[0] {
                    "pkg_name" => name = Some(parts[1].to_string()),
                    "pkg_version" => version = Some(parts[1].to_string()),
                    _ => (),
                }
            }
        }

        // Only the name is required to be present initiallly in the plan.sh
        if name.is_none() {
            return Err(Error::PlanMalformed);
        }

        // Default the version to 'undefined' if it's not present
        let v = if version.is_none() {
            String::from("undefined")
        } else {
            version.unwrap()
        };

        let plan = Plan::new(name.unwrap(), v);
        Ok(plan)
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// A simple test of starting the runtime manually

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    do core::rt::start(argc, argv, crate_map) {
        debug!("creating my own runtime is joy");
    }
}<commit_msg>xfail-fast run-pass/core-rt-smoke<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-fast

// A simple test of starting the runtime manually

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    do core::rt::start(argc, argv, crate_map) {
        debug!("creating my own runtime is joy");
    }
}<|endoftext|>