<commit_before>//! This module contains a platform specific implementation of the window shell.

use std::{
    cell::{Cell, RefCell},
    char,
    collections::HashMap,
    rc::Rc,
    sync::Mutex,
};

#[cfg(not(target_os = "redox"))]
use minifb;

#[cfg(redox)]
use minifb_fix as minifb;

use spin_sleep::LoopHelper;

use crate::{prelude::*, render::*, utils::*};

pub fn initialize() {}

fn key_event_helper(key: &mut KeyHelper, adapter: &mut A, window: &minifb::Window)
where
    A: WindowAdapter,
{
    if !key.0 && window.is_key_down(key.1) {
        adapter.key_event(KeyEvent {
            key: key.2,
            state: ButtonState::Down,
            text: String::new(),
        });

        key.0 = true;
    } else {
        key.0 = false;
    }
}

fn unicode_to_key_event(uni_char: u32) -> KeyEvent {
    let mut text = String::new();

    let key = if let Some(character) = char::from_u32(uni_char) {
        text = character.to_string();
        Key::from(character)
    } else {
        Key::Unknown
    };

    if key == Key::Up || key == Key::Down || key == Key::Left || key == Key::Right {
        text = String::new()
    }

    KeyEvent {
        key,
        state: ButtonState::Down,
        text,
    }
}

struct KeyInputCallBack {
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
}

impl minifb::InputCallback for KeyInputCallBack {
    fn add_char(&mut self, uni_char: u32) {
        self.key_events
            .borrow_mut()
            .push(unicode_to_key_event(uni_char));
    }
}

struct KeyHelper(bool, minifb::Key, Key);

/// Concrete implementation of the window shell.
pub struct WindowShell
where
    A: WindowAdapter,
{
    window: minifb::Window,
    render_context_2_d: RenderContext2D,
    adapter: A,
    mouse_pos: (f32, f32),
    button_down: (bool, bool, bool),
    window_size: (usize, usize),
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
    // todo: temp solution
    key_backspace: KeyHelper,
    key_delete: KeyHelper,
    key_enter: KeyHelper,
    key_control: KeyHelper,
    key_shift_l: KeyHelper,
    key_shift_r: KeyHelper,
    key_alt: KeyHelper,
}

impl WindowShell
where
    A: WindowAdapter,
{
    /// Creates a new window shell with an adapter.
    pub fn new(
        window: minifb::Window,
        adapter: A,
        key_events: Rc<RefCell<Vec<KeyEvent>>>,
    ) -> WindowShell {
        let size = window.get_size();
        let render_context_2_d = RenderContext2D::new(size.0 as f64, size.1 as f64);

        WindowShell {
            window,
            // window_size: window_size,
            // mouse_buttons: (false, false, false),
            // mouse_position: Point::default(),
            render_context_2_d,
            adapter,
            mouse_pos: (0.0, 0.0),
            window_size: size,
            button_down: (false, false, false),
            key_events,
            key_backspace: KeyHelper(false, minifb::Key::Backspace, Key::Backspace),
            key_delete: KeyHelper(false, minifb::Key::Delete, Key::Delete),
            key_enter: KeyHelper(false, minifb::Key::Enter, Key::Enter),
            key_control: KeyHelper(false, minifb::Key::LeftCtrl, Key::Control),
            key_shift_l: KeyHelper(false, minifb::Key::LeftShift, Key::ShiftL),
            key_shift_r: KeyHelper(false, minifb::Key::RightShift, Key::ShiftR),
            key_alt: KeyHelper(false, minifb::Key::LeftAlt, Key::Alt),
        }
    }

    /// Gets the shell adapter.
    pub fn adapter(&mut self) -> &mut A {
        &mut self.adapter
    }

    /// Gets the render context 2D.
    pub fn render_context_2_d(&mut self) -> &mut RenderContext2D {
        &mut self.render_context_2_d
    }

    fn drain_events(&mut self) {
        // mouse move
        if let Some(pos) = self.window.get_mouse_pos(minifb::MouseMode::Discard) {
            if pos != self.mouse_pos {
                self.adapter.mouse(pos.0 as f64, pos.1 as f64);
                self.mouse_pos = pos;
            }
        }

        // mouse
        let left_button_down = self.window.get_mouse_down(minifb::MouseButton::Left);
        let middle_button_down = self.window.get_mouse_down(minifb::MouseButton::Middle);
        let right_button_down = self.window.get_mouse_down(minifb::MouseButton::Right);

        if left_button_down != self.button_down.0 {
            if left_button_down {
                self.push_mouse_event(true, MouseButton::Left);
            } else {
                self.push_mouse_event(false, MouseButton::Left);
            }
            self.button_down.0 = left_button_down;
        }

        if middle_button_down != self.button_down.1 {
            if middle_button_down {
                self.push_mouse_event(true, MouseButton::Middle);
            } else {
                self.push_mouse_event(false, MouseButton::Middle);
            }
            self.button_down.1 = middle_button_down;
        }

        if right_button_down != self.button_down.2 {
            if right_button_down {
                self.push_mouse_event(true, MouseButton::Right);
            } else {
                self.push_mouse_event(false, MouseButton::Right);
            }
            self.button_down.2 = right_button_down;
        }

        // scroll
        if let Some(delta) = self.window.get_scroll_wheel() {
            self.adapter.scroll(delta.0 as f64, delta.1 as f64);
        }

        // key
        while let Some(event) = self.key_events.borrow_mut().pop() {
            self.adapter.key_event(event);
        }

        key_event_helper(&mut self.key_backspace, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_delete, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_enter, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_control, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_shift_l, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_shift_r, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_alt, &mut self.adapter, &mut self.window);

        // resize
        if self.window_size != self.window.get_size() {
            self.window_size = self.window.get_size();
            self.render_context_2_d
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
            self.adapter
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
        }
    }

    fn push_mouse_event(&mut self, pressed: bool, button: MouseButton) {
        let state = if pressed {
            ButtonState::Down
        } else {
            ButtonState::Up
        };

        self.adapter.mouse_event(MouseEvent {
            x: self.mouse_pos.0 as f64,
            y: self.mouse_pos.1 as f64,
            button,
            state,
        });
    }

    pub fn flip(&mut self) -> bool {
        if let Some(data) = self.render_context_2_d.data() {
            self.window.update_with_buffer(data).unwrap();
            // CONSOLE.time_end("render");
            return true;
        }

        false
    }
}

impl Drop for WindowShell
where
    A: WindowAdapter,
{
    fn drop(&mut self) {}
}

/// Implementation of the OrbClient based shell runner.
pub struct ShellRunner
where
    A: WindowAdapter,
{
    pub window_shell: Rc<RefCell<WindowShell>>,
    pub update: Rc<Cell<bool>>,
    pub running: Rc<Cell<bool>>,
    pub updater: Box<dyn Updater>,
}

impl ShellRunner
where
    A: WindowAdapter,
{
    pub fn run(&mut self) {
        let mut loop_helper = LoopHelper::builder()
            .report_interval_s(0.5)
            .build_with_target_rate(60.0);

        let mut _current_fps = None;

        loop {
            if !self.running.get() || !self.window_shell.borrow().window.is_open() {
                break;
            }

            let _delta = loop_helper.loop_start();

            if let Some(fps) = loop_helper.report_rate() {
                _current_fps = Some(fps);
                // println!("fps: {}", fps);
            }

            // CONSOLE.time("complete run");

            self.updater.update();

            if self.update.get() {
                self.update.set(false);
            }

            if !self.window_shell.borrow_mut().flip() {
                self.window_shell.borrow_mut().window.update();
            }

            self.window_shell.borrow_mut().drain_events();

            loop_helper.loop_sleep();
            // CONSOLE.time_end("complete run");
        }
    }
}

/// Constructs the window shell
pub struct WindowBuilder
where
    A: WindowAdapter,
{
    title: String,

    resizeable: bool,

    bounds: Rect,

    adapter: A,
}

impl WindowBuilder
where
    A: WindowAdapter,
{
    /// Create a new window builder with the given adapter.
    pub fn new(adapter: A) -> Self {
        WindowBuilder {
            adapter,
            title: String::default(),
            resizeable: false,
            bounds: Rect::default(),
        }
    }

    /// Sets the title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    /// Sets resizeable.
    pub fn resizeable(mut self, resizeable: bool) -> Self {
        self.resizeable = resizeable;
        self
    }

    /// Sets the bounds.
    pub fn bounds(mut self, bounds: impl Into<Rect>) -> Self {
        self.bounds = bounds.into();
        self
    }

    /// Builds the window shell.
    pub fn build(self) -> WindowShell {
        let window_options = minifb::WindowOptions {
            resize: self.resizeable,
            ..Default::default()
        };

        let mut window = minifb::Window::new(
            self.title.as_str(),
            self.bounds.width as usize,
            self.bounds.height as usize,
            window_options,
        )
        .unwrap_or_else(|e| {
            panic!("{}", e);
        });

        let key_events = Rc::new(RefCell::new(vec![]));

        window.set_input_callback(Box::new(KeyInputCallBack {
            key_events: key_events.clone(),
        }));

        window.set_position(self.bounds.x as isize, self.bounds.y as isize);

        WindowShell::new(window, self.adapter, key_events)
    }
}

use std::time::Instant;

lazy_static! {
    pub static ref CONSOLE: Console = Console {
        instants: Mutex::new(HashMap::new())
    };
}

pub struct Console {
    instants: Mutex<HashMap<String, Instant>>,
}

impl Console {
    pub fn time(&self, name: impl Into<String>) {
        self.instants
            .lock()
            .unwrap()
            .insert(name.into(), Instant::now());
    }

    pub fn time_end(&self, name: impl Into<String>) {
        if let Some((k, v)) = self.instants.lock().unwrap().remove_entry(&name.into()) {
            println!("{} {}ms - timer ended", k, v.elapsed().as_millis());
        }
    }

    pub fn log(&self, message: impl Into<String>) {
        println!("{}", message.into());
    }
}
<commit_msg>Redox fix 100.<commit_after>//! This module contains a platform specific implementation of the window shell.

use std::{
    cell::{Cell, RefCell},
    char,
    collections::HashMap,
    rc::Rc,
    sync::Mutex,
};

#[cfg(not(target_os = "redox"))]
use minifb;

#[cfg(target_os = "redox")]
use minifb_fix as minifb;

use spin_sleep::LoopHelper;

use crate::{prelude::*, render::*, utils::*};

pub fn initialize() {}

fn key_event_helper(key: &mut KeyHelper, adapter: &mut A, window: &minifb::Window)
where
    A: WindowAdapter,
{
    if !key.0 && window.is_key_down(key.1) {
        adapter.key_event(KeyEvent {
            key: key.2,
            state: ButtonState::Down,
            text: String::new(),
        });

        key.0 = true;
    } else {
        key.0 = false;
    }
}

fn unicode_to_key_event(uni_char: u32) -> KeyEvent {
    let mut text = String::new();

    let key = if let Some(character) = char::from_u32(uni_char) {
        text = character.to_string();
        Key::from(character)
    } else {
        Key::Unknown
    };

    if key == Key::Up || key == Key::Down || key == Key::Left || key == Key::Right {
        text = String::new()
    }

    KeyEvent {
        key,
        state: ButtonState::Down,
        text,
    }
}

struct KeyInputCallBack {
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
}

impl minifb::InputCallback for KeyInputCallBack {
    fn add_char(&mut self, uni_char: u32) {
        self.key_events
            .borrow_mut()
            .push(unicode_to_key_event(uni_char));
    }
}

struct KeyHelper(bool, minifb::Key, Key);

/// Concrete implementation of the window shell.
pub struct WindowShell
where
    A: WindowAdapter,
{
    window: minifb::Window,
    render_context_2_d: RenderContext2D,
    adapter: A,
    mouse_pos: (f32, f32),
    button_down: (bool, bool, bool),
    window_size: (usize, usize),
    key_events: Rc<RefCell<Vec<KeyEvent>>>,
    // todo: temp solution
    key_backspace: KeyHelper,
    key_delete: KeyHelper,
    key_enter: KeyHelper,
    key_control: KeyHelper,
    key_shift_l: KeyHelper,
    key_shift_r: KeyHelper,
    key_alt: KeyHelper,
}

impl WindowShell
where
    A: WindowAdapter,
{
    /// Creates a new window shell with an adapter.
    pub fn new(
        window: minifb::Window,
        adapter: A,
        key_events: Rc<RefCell<Vec<KeyEvent>>>,
    ) -> WindowShell {
        let size = window.get_size();
        let render_context_2_d = RenderContext2D::new(size.0 as f64, size.1 as f64);

        WindowShell {
            window,
            // window_size: window_size,
            // mouse_buttons: (false, false, false),
            // mouse_position: Point::default(),
            render_context_2_d,
            adapter,
            mouse_pos: (0.0, 0.0),
            window_size: size,
            button_down: (false, false, false),
            key_events,
            key_backspace: KeyHelper(false, minifb::Key::Backspace, Key::Backspace),
            key_delete: KeyHelper(false, minifb::Key::Delete, Key::Delete),
            key_enter: KeyHelper(false, minifb::Key::Enter, Key::Enter),
            key_control: KeyHelper(false, minifb::Key::LeftCtrl, Key::Control),
            key_shift_l: KeyHelper(false, minifb::Key::LeftShift, Key::ShiftL),
            key_shift_r: KeyHelper(false, minifb::Key::RightShift, Key::ShiftR),
            key_alt: KeyHelper(false, minifb::Key::LeftAlt, Key::Alt),
        }
    }

    /// Gets the shell adapter.
    pub fn adapter(&mut self) -> &mut A {
        &mut self.adapter
    }

    /// Gets the render context 2D.
    pub fn render_context_2_d(&mut self) -> &mut RenderContext2D {
        &mut self.render_context_2_d
    }

    fn drain_events(&mut self) {
        // mouse move
        if let Some(pos) = self.window.get_mouse_pos(minifb::MouseMode::Discard) {
            if pos != self.mouse_pos {
                self.adapter.mouse(pos.0 as f64, pos.1 as f64);
                self.mouse_pos = pos;
            }
        }

        // mouse
        let left_button_down = self.window.get_mouse_down(minifb::MouseButton::Left);
        let middle_button_down = self.window.get_mouse_down(minifb::MouseButton::Middle);
        let right_button_down = self.window.get_mouse_down(minifb::MouseButton::Right);

        if left_button_down != self.button_down.0 {
            if left_button_down {
                self.push_mouse_event(true, MouseButton::Left);
            } else {
                self.push_mouse_event(false, MouseButton::Left);
            }
            self.button_down.0 = left_button_down;
        }

        if middle_button_down != self.button_down.1 {
            if middle_button_down {
                self.push_mouse_event(true, MouseButton::Middle);
            } else {
                self.push_mouse_event(false, MouseButton::Middle);
            }
            self.button_down.1 = middle_button_down;
        }

        if right_button_down != self.button_down.2 {
            if right_button_down {
                self.push_mouse_event(true, MouseButton::Right);
            } else {
                self.push_mouse_event(false, MouseButton::Right);
            }
            self.button_down.2 = right_button_down;
        }

        // scroll
        if let Some(delta) = self.window.get_scroll_wheel() {
            self.adapter.scroll(delta.0 as f64, delta.1 as f64);
        }

        // key
        while let Some(event) = self.key_events.borrow_mut().pop() {
            self.adapter.key_event(event);
        }

        key_event_helper(&mut self.key_backspace, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_delete, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_enter, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_control, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_shift_l, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_shift_r, &mut self.adapter, &mut self.window);
        key_event_helper(&mut self.key_alt, &mut self.adapter, &mut self.window);

        // resize
        if self.window_size != self.window.get_size() {
            self.window_size = self.window.get_size();
            self.render_context_2_d
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
            self.adapter
                .resize(self.window_size.0 as f64, self.window_size.1 as f64);
        }
    }

    fn push_mouse_event(&mut self, pressed: bool, button: MouseButton) {
        let state = if pressed {
            ButtonState::Down
        } else {
            ButtonState::Up
        };

        self.adapter.mouse_event(MouseEvent {
            x: self.mouse_pos.0 as f64,
            y: self.mouse_pos.1 as f64,
            button,
            state,
        });
    }

    pub fn flip(&mut self) -> bool {
        if let Some(data) = self.render_context_2_d.data() {
            self.window.update_with_buffer(data).unwrap();
            // CONSOLE.time_end("render");
            return true;
        }

        false
    }
}

impl Drop for WindowShell
where
    A: WindowAdapter,
{
    fn drop(&mut self) {}
}

/// Implementation of the OrbClient based shell runner.
pub struct ShellRunner
where
    A: WindowAdapter,
{
    pub window_shell: Rc<RefCell<WindowShell>>,
    pub update: Rc<Cell<bool>>,
    pub running: Rc<Cell<bool>>,
    pub updater: Box<dyn Updater>,
}

impl ShellRunner
where
    A: WindowAdapter,
{
    pub fn run(&mut self) {
        let mut loop_helper = LoopHelper::builder()
            .report_interval_s(0.5)
            .build_with_target_rate(60.0);

        let mut _current_fps = None;

        loop {
            if !self.running.get() || !self.window_shell.borrow().window.is_open() {
                break;
            }

            let _delta = loop_helper.loop_start();

            if let Some(fps) = loop_helper.report_rate() {
                _current_fps = Some(fps);
                // println!("fps: {}", fps);
            }

            // CONSOLE.time("complete run");

            self.updater.update();

            if self.update.get() {
                self.update.set(false);
            }

            if !self.window_shell.borrow_mut().flip() {
                self.window_shell.borrow_mut().window.update();
            }

            self.window_shell.borrow_mut().drain_events();

            loop_helper.loop_sleep();
            // CONSOLE.time_end("complete run");
        }
    }
}

/// Constructs the window shell
pub struct WindowBuilder
where
    A: WindowAdapter,
{
    title: String,

    resizeable: bool,

    bounds: Rect,

    adapter: A,
}

impl WindowBuilder
where
    A: WindowAdapter,
{
    /// Create a new window builder with the given adapter.
    pub fn new(adapter: A) -> Self {
        WindowBuilder {
            adapter,
            title: String::default(),
            resizeable: false,
            bounds: Rect::default(),
        }
    }

    /// Sets the title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    /// Sets resizeable.
    pub fn resizeable(mut self, resizeable: bool) -> Self {
        self.resizeable = resizeable;
        self
    }

    /// Sets the bounds.
    pub fn bounds(mut self, bounds: impl Into<Rect>) -> Self {
        self.bounds = bounds.into();
        self
    }

    /// Builds the window shell.
    pub fn build(self) -> WindowShell {
        let window_options = minifb::WindowOptions {
            resize: self.resizeable,
            ..Default::default()
        };

        let mut window = minifb::Window::new(
            self.title.as_str(),
            self.bounds.width as usize,
            self.bounds.height as usize,
            window_options,
        )
        .unwrap_or_else(|e| {
            panic!("{}", e);
        });

        let key_events = Rc::new(RefCell::new(vec![]));

        window.set_input_callback(Box::new(KeyInputCallBack {
            key_events: key_events.clone(),
        }));

        window.set_position(self.bounds.x as isize, self.bounds.y as isize);

        WindowShell::new(window, self.adapter, key_events)
    }
}

use std::time::Instant;

lazy_static! {
    pub static ref CONSOLE: Console = Console {
        instants: Mutex::new(HashMap::new())
    };
}

pub struct Console {
    instants: Mutex<HashMap<String, Instant>>,
}

impl Console {
    pub fn time(&self, name: impl Into<String>) {
        self.instants
            .lock()
            .unwrap()
            .insert(name.into(), Instant::now());
    }

    pub fn time_end(&self, name: impl Into<String>) {
        if let Some((k, v)) = self.instants.lock().unwrap().remove_entry(&name.into()) {
            println!("{} {}ms - timer ended", k, v.elapsed().as_millis());
        }
    }

    pub fn log(&self, message: impl Into<String>) {
        println!("{}", message.into());
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use time;
use crate::{db::connect_db, error::Result};
use failure::err_msg;

/// Fields we need use in cratesfyi
#[derive(Debug)]
struct GitHubFields {
    description: String,
    stars: i64,
    forks: i64,
    issues: i64,
    last_commit: time::Timespec,
}

/// Updates github fields in crates table
pub fn github_updater() -> Result<()> {
    let conn = connect_db()?;

    // TODO: This query assumes repository field in Cargo.toml is
    //       always the same across all versions of a crate
    for row in &conn.query("SELECT DISTINCT ON (crates.name)
                                        crates.name,
                                        crates.id,
                                        releases.repository_url
                                 FROM crates
                                 INNER JOIN releases ON releases.crate_id = crates.id
                                 WHERE releases.repository_url ~ '^https*://github.com' AND
                                       (crates.github_last_update < NOW() - INTERVAL '1 day' OR
                                        crates.github_last_update IS NULL)
                                 ORDER BY crates.name, releases.release_time DESC",
                                &[])? {
        let crate_name: String = row.get(0);
        let crate_id: i32 = row.get(1);
        let repository_url: String = row.get(2);

        if let Err(err) = get_github_path(&repository_url[..])
            .ok_or_else(|| err_msg("Failed to get github path"))
            .and_then(|path| get_github_fields(&path[..]))
            .and_then(|fields| {
                conn.execute("UPDATE crates
                              SET github_description = $1,
                                  github_stars = $2, github_forks = $3,
                                  github_issues = $4, github_last_commit = $5,
                                  github_last_update = NOW() WHERE id = $6",
                             &[&fields.description,
                               &(fields.stars as i32),
                               &(fields.forks as i32),
                               &(fields.issues as i32),
                               &(fields.last_commit),
                               &crate_id])
                    .or_else(|e| Err(e.into()))
            }) {
            debug!("Failed to update github fields of: {} {}", crate_name, err);
        }

        // sleep for rate limits
        use std::thread;
        use std::time::Duration;
        thread::sleep(Duration::from_secs(2));
    }

    Ok(())
}

fn get_github_fields(path: &str) -> Result<GitHubFields> {
    use rustc_serialize::json::Json;

    let body = {
        use std::io::Read;
        use reqwest::{Client, StatusCode};
        use reqwest::header::USER_AGENT;
        use std::env;

        let client = Client::new();
        let mut body = String::new();

        let mut resp = client.get(&format!("https://api.github.com/repos/{}", path)[..])
            .header(USER_AGENT, format!("cratesfyi/{}", env!("CARGO_PKG_VERSION")))
            .basic_auth(
                env::var("CRATESFYI_GITHUB_USERNAME")
                    .ok()
                    .and_then(|u| Some(u.to_string()))
                    .unwrap_or("".to_string()),
                env::var("CRATESFYI_GITHUB_ACCESSTOKEN").ok(),
            )
            .send()?;

        if resp.status() != StatusCode::OK {
            return Err(err_msg("Failed to get github data"));
        }

        resp.read_to_string(&mut body)?;
        body
    };

    let json = Json::from_str(&body[..])?;
    let obj = json.as_object().unwrap();

    Ok(GitHubFields {
        description: obj.get("description").and_then(|d| d.as_string()).unwrap_or("").to_string(),
        stars: obj.get("stargazers_count").and_then(|d| d.as_i64()).unwrap_or(0),
        forks: obj.get("forks_count").and_then(|d| d.as_i64()).unwrap_or(0),
        issues: obj.get("open_issues").and_then(|d| d.as_i64()).unwrap_or(0),
        last_commit: time::strptime(obj.get("pushed_at")
                                        .and_then(|d| d.as_string())
                                        .unwrap_or(""),
                                    "%Y-%m-%dT%H:%M:%S")
            .unwrap_or_else(|_| time::now())
            .to_timespec(),
    })
}

fn get_github_path(url: &str) -> Option<String> {
    let re = Regex::new(r"https?://github\.com/([\w\._-]+)/([\w\._-]+)").unwrap();
    match re.captures(url) {
        Some(cap) => {
            let username = cap.get(1).unwrap().as_str();
            let reponame = cap.get(2).unwrap().as_str();
            Some(format!("{}/{}",
                         username,
                         if reponame.ends_with(".git") {
                             reponame.split(".git").next().unwrap()
                         } else {
                             reponame
                         }))
        }
        None => None,
    }
}

#[cfg(test)]
mod test {
    use super::{get_github_path, get_github_fields, github_updater};

    #[test]
    fn test_get_github_path() {
        assert_eq!(get_github_path("https://github.com/onur/cratesfyi"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("http://github.com/onur/cratesfyi"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("https://github.com/onur/cratesfyi.git"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("https://github.com/onur23cmD_M_R_L_/crates_fy-i"),
                   Some("onur23cmD_M_R_L_/crates_fy-i".to_string()));
        assert_eq!(get_github_path("https://github.com/docopt/docopt.rs"),
                   Some("docopt/docopt.rs".to_string()));
    }

    #[test]
    #[ignore]
    fn test_get_github_fields() {
        let _ = env_logger::try_init();
        let fields = get_github_fields("onur/cratesfyi");
        assert!(fields.is_ok());

        let fields = fields.unwrap();
        assert!(fields.description != "".to_string());
        assert!(fields.stars >= 0);
        assert!(fields.forks >= 0);
        assert!(fields.issues >= 0);

        use time;
        assert!(fields.last_commit <= time::now().to_timespec());
    }

    #[test]
    #[ignore]
    fn test_github_updater() {
        let _ = env_logger::try_init();
        assert!(github_updater().is_ok());
    }
}
<commit_msg>Remove confusing `map()`<commit_after>use regex::Regex;
use time;
use crate::{db::connect_db, error::Result};
use failure::err_msg;

/// Fields we need use in cratesfyi
#[derive(Debug)]
struct GitHubFields {
    description: String,
    stars: i64,
    forks: i64,
    issues: i64,
    last_commit: time::Timespec,
}

/// Updates github fields in crates table
pub fn github_updater() -> Result<()> {
    let conn = connect_db()?;

    // TODO: This query assumes repository field in Cargo.toml is
    //       always the same across all versions of a crate
    for row in &conn.query("SELECT DISTINCT ON (crates.name)
                                        crates.name,
                                        crates.id,
                                        releases.repository_url
                                 FROM crates
                                 INNER JOIN releases ON releases.crate_id = crates.id
                                 WHERE releases.repository_url ~ '^https*://github.com' AND
                                       (crates.github_last_update < NOW() - INTERVAL '1 day' OR
                                        crates.github_last_update IS NULL)
                                 ORDER BY crates.name, releases.release_time DESC",
                                &[])? {
        let crate_name: String = row.get(0);
        let crate_id: i32 = row.get(1);
        let repository_url: String = row.get(2);

        if let Err(err) = get_github_path(&repository_url[..])
            .ok_or_else(|| err_msg("Failed to get github path"))
            .and_then(|path| get_github_fields(&path[..]))
            .and_then(|fields| {
                conn.execute("UPDATE crates
                              SET github_description = $1,
                                  github_stars = $2, github_forks = $3,
                                  github_issues = $4, github_last_commit = $5,
                                  github_last_update = NOW() WHERE id = $6",
                             &[&fields.description,
                               &(fields.stars as i32),
                               &(fields.forks as i32),
                               &(fields.issues as i32),
                               &(fields.last_commit),
                               &crate_id])
                    .or_else(|e| Err(e.into()))
            }) {
            debug!("Failed to update github fields of: {} {}", crate_name, err);
        }

        // sleep for rate limits
        use std::thread;
        use std::time::Duration;
        thread::sleep(Duration::from_secs(2));
    }

    Ok(())
}

fn get_github_fields(path: &str) -> Result<GitHubFields> {
    use rustc_serialize::json::Json;

    let body = {
        use std::io::Read;
        use reqwest::{Client, StatusCode};
        use reqwest::header::USER_AGENT;
        use std::env;

        let client = Client::new();
        let mut body = String::new();

        let mut resp = client.get(&format!("https://api.github.com/repos/{}", path)[..])
            .header(USER_AGENT, format!("cratesfyi/{}", env!("CARGO_PKG_VERSION")))
            .basic_auth(
                env::var("CRATESFYI_GITHUB_USERNAME")
                    .ok()
                    .unwrap_or_default(),
                env::var("CRATESFYI_GITHUB_ACCESSTOKEN").ok(),
            )
            .send()?;

        if resp.status() != StatusCode::OK {
            return Err(err_msg("Failed to get github data"));
        }

        resp.read_to_string(&mut body)?;
        body
    };

    let json = Json::from_str(&body[..])?;
    let obj = json.as_object().unwrap();

    Ok(GitHubFields {
        description: obj.get("description").and_then(|d| d.as_string()).unwrap_or("").to_string(),
        stars: obj.get("stargazers_count").and_then(|d| d.as_i64()).unwrap_or(0),
        forks: obj.get("forks_count").and_then(|d| d.as_i64()).unwrap_or(0),
        issues: obj.get("open_issues").and_then(|d| d.as_i64()).unwrap_or(0),
        last_commit: time::strptime(obj.get("pushed_at")
                                        .and_then(|d| d.as_string())
                                        .unwrap_or(""),
                                    "%Y-%m-%dT%H:%M:%S")
            .unwrap_or_else(|_| time::now())
            .to_timespec(),
    })
}

fn get_github_path(url: &str) -> Option<String> {
    let re = Regex::new(r"https?://github\.com/([\w\._-]+)/([\w\._-]+)").unwrap();
    match re.captures(url) {
        Some(cap) => {
            let username = cap.get(1).unwrap().as_str();
            let reponame = cap.get(2).unwrap().as_str();
            Some(format!("{}/{}",
                         username,
                         if reponame.ends_with(".git") {
                             reponame.split(".git").next().unwrap()
                         } else {
                             reponame
                         }))
        }
        None => None,
    }
}

#[cfg(test)]
mod test {
    use super::{get_github_path, get_github_fields, github_updater};

    #[test]
    fn test_get_github_path() {
        assert_eq!(get_github_path("https://github.com/onur/cratesfyi"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("http://github.com/onur/cratesfyi"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("https://github.com/onur/cratesfyi.git"),
                   Some("onur/cratesfyi".to_string()));
        assert_eq!(get_github_path("https://github.com/onur23cmD_M_R_L_/crates_fy-i"),
                   Some("onur23cmD_M_R_L_/crates_fy-i".to_string()));
        assert_eq!(get_github_path("https://github.com/docopt/docopt.rs"),
                   Some("docopt/docopt.rs".to_string()));
    }

    #[test]
    #[ignore]
    fn test_get_github_fields() {
        let _ = env_logger::try_init();
        let fields = get_github_fields("onur/cratesfyi");
        assert!(fields.is_ok());

        let fields = fields.unwrap();
        assert!(fields.description != "".to_string());
        assert!(fields.stars >= 0);
        assert!(fields.forks >= 0);
        assert!(fields.issues >= 0);

        use time;
        assert!(fields.last_commit <= time::now().to_timespec());
    }

    #[test]
    #[ignore]
    fn test_github_updater() {
        let _ = env_logger::try_init();
        assert!(github_updater().is_ok());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Pierre Talbot (IRCAM)

// 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.

#![crate_name = "interval"]
#![experimental]
// #![comment = "Interval Arithmetic Library"]
// #![license = "Apache v2"]
#![crate_type = "dylib"]

pub mod interval;
<commit_msg>Fix warnings generated by the latest Rust version.<commit_after>// Copyright 2015 Pierre Talbot (IRCAM)

// 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.

#![crate_name = "interval"]
#![unstable]
// #![comment = "Interval Arithmetic Library"]
// #![license = "Apache v2"]
#![crate_type = "dylib"]

#![feature(core)]

pub mod interval;
<|endoftext|>


<commit_before>//Implements http://rosettacode.org/wiki/9_billion_names_of_God_the_integer

extern crate num;

use num::bigint::BigUint;
use std::string::String;
use std::cmp::min;

fn cumu<'a>(num: uint, cache: &'a mut Vec<Vec<BigUint>>) -> &'a Vec<BigUint> {
    let len = cache.len();
    for l in range(len, num+1) {
	let initial_value:BigUint = from_str("0").unwrap();
	let mut r: Vec<BigUint> = vec!(initial_value);

	for x in range(1, l+1) {
	    let y = r.get(x -1).clone();
	    let z = cache.get(l-x).get(min(x, l-x)).clone();
	    let w = y+z;

	    r.push(w)
	}
	cache.push(r);
    }
    cache.get(num)
}

// Returns a line
fn row(num: uint,  cache: &mut Vec<Vec<BigUint>>) -> String {
    let r = cumu(num,cache);
    let mut returned_string = String::new();
    for i in range(0,num) {
        let i = *r.get(i+1) - *r.get (i);
        returned_string.push_str(i.to_string().as_slice());
        returned_string.push_str(", ");
    }
    returned_string
}

#[cfg(not(test))]
fn main() {
    let mut cache: Vec<Vec<BigUint>> = Vec::new();
    let initial_value:BigUint = from_str("1").unwrap();
    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    println!("rows");
    for n in range(1u, 11) {
        let x = n;
        println!("{}: {}", n, row(x,&mut cache));
    }

    println!("sums");

    let x: Vec<uint> = vec!(23, 123, 1234, 12345);
    for y in x.iter() {
	let z = cumu(*y,&mut cache);
	let w = z.last();
	println!("{}: {}", y, w.unwrap());
    }
}

#[test]
fn test_cumu() {
    let mut cache: Vec<Vec<BigUint>> = Vec::new();

    let initial_value:BigUint = from_str("1").unwrap();

    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    let a: Vec<uint> = vec!(23, 123, 1234);
    let b: Vec<BigUint> = vec!(
        from_str("1255").unwrap(),
        from_str("2552338241").unwrap(),
        from_str("156978797223733228787865722354959930").unwrap());

    let mut n=0;
    for y in a.iter() {
	let z = cumu(*y,&mut cache);
	let w = z.last().unwrap();
	assert!(w == b.get(n));
	n= n+1;
    }
}

#[test]
fn test_row() {

    let mut cache: Vec<Vec<BigUint>> = Vec::new();

    let initial_value:BigUint = from_str("1").unwrap();

    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    let a: String = from_str("1, 2, 1, 1, ").unwrap();

    let x = 4;
    assert!(a == row(x,&mut cache));

}
<commit_msg>9 billion... fix .get to indexing.<commit_after>//Implements http://rosettacode.org/wiki/9_billion_names_of_God_the_integer

extern crate num;

use num::bigint::BigUint;
use std::string::String;
use std::cmp::min;

fn cumu<'a>(num: uint, cache: &'a mut Vec<Vec<BigUint>>) -> &'a Vec<BigUint> {
    let len = cache.len();
    for l in range(len, num+1) {
        let initial_value:BigUint = from_str("0").unwrap();
        let mut r: Vec<BigUint> = vec!(initial_value);
        for x in range(1, l+1) {
            let y = r[x -1].clone();
            let z = (*cache)[l-x][min(x, l-x)].clone();
            let w = y+z;
            r.push(w)
        }
        cache.push(r);
    }
    &(*cache)[num]
}

// Returns a line
fn row(num: uint,  cache: &mut Vec<Vec<BigUint>>) -> String {
    let r = cumu(num,cache);
    let mut returned_string = String::new();
    for i in range(0,num) {
        let i = (*r)[i+1] - (*r)[i];
        returned_string.push_str(i.to_string().as_slice());
        returned_string.push_str(", ");
    }
    returned_string
}

#[cfg(not(test))]
fn main() {
    let mut cache: Vec<Vec<BigUint>> = Vec::new();
    let initial_value:BigUint = from_str("1").unwrap();
    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    println!("rows");
    for n in range(1u, 11) {
        let x = n;
        println!("{}: {}", n, row(x,&mut cache));
    }

    println!("sums");

    let x: Vec<uint> = vec!(23, 123, 1234, 12345);
    for y in x.iter() {
  let z = cumu(*y,&mut cache);
  let w = z.last();
  println!("{}: {}", y, w.unwrap());
    }
}

#[test]
fn test_cumu() {
    let mut cache: Vec<Vec<BigUint>> = Vec::new();

    let initial_value:BigUint = from_str("1").unwrap();

    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    let a: Vec<uint> = vec!(23, 123, 1234);
    let b: Vec<BigUint> = vec!(
        from_str("1255").unwrap(),
        from_str("2552338241").unwrap(),
        from_str("156978797223733228787865722354959930").unwrap());

    let mut n=0;
    for y in a.iter() {
  let z = cumu(*y,&mut cache);
  let w = z.last().unwrap();
  assert!(w == b.get(n));
  n= n+1;
    }
}

#[test]
fn test_row() {

    let mut cache: Vec<Vec<BigUint>> = Vec::new();

    let initial_value:BigUint = from_str("1").unwrap();

    let initial_vector : Vec<BigUint> = vec!(initial_value);
    cache.push(initial_vector);

    let a: String = from_str("1, 2, 1, 1, ").unwrap();

    let x = 4;
    assert!(a == row(x,&mut cache));

}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    emitter::{
        stats::StatsAccumulator, wait_for_accounts_sequence, MAX_TXN_BATCH_SIZE,
        TRANSACTIONS_PER_ACCOUNT, TXN_EXPIRATION_SECONDS,
    },
    transaction_generator::TransactionGenerator,
    EmitThreadParams,
};
use aptos_logger::sample::SampleRate;
use aptos_logger::sample::Sampling;
use aptos_logger::{debug, sample, warn};
use aptos_rest_client::Client as RestClient;
use aptos_sdk::{
    move_types::account_address::AccountAddress,
    types::{transaction::SignedTransaction, LocalAccount},
};
use core::{
    cmp::{max, min},
    result::Result::{Err, Ok},
    sync::atomic::{AtomicBool, Ordering},
    time::Duration,
};
use futures::future::try_join_all;
use rand::seq::IteratorRandom;
use rand::Rng;
use std::sync::atomic::AtomicU64;
use std::{sync::Arc, time::Instant};
use tokio::time::sleep;

#[derive(Debug)]
pub struct SubmissionWorker {
    pub(crate) accounts: Vec<LocalAccount>,
    client: RestClient,
    all_addresses: Arc<Vec<AccountAddress>>,
    stop: Arc<AtomicBool>,
    params: EmitThreadParams,
    stats: Arc<StatsAccumulator>,
    txn_generator: Box<dyn TransactionGenerator>,
    invalid_transaction_ratio: usize,
    rng: ::rand::rngs::StdRng,
}

// Note, there is an edge case that can occur if the transaction emitter
// bursts the target node too fast, and the emitter doesn't handle it
// very well, instead waiting up until the timeout for the target seqnum
// to progress, even though it never will. See more here:
// https://github.com/aptos-labs/aptos-core/issues/
impl SubmissionWorker {
    pub fn new(
        accounts: Vec<LocalAccount>,
        client: RestClient,
        all_addresses: Arc<Vec<AccountAddress>>,
        stop: Arc<AtomicBool>,
        params: EmitThreadParams,
        stats: Arc<StatsAccumulator>,
        txn_generator: Box<dyn TransactionGenerator>,
        invalid_transaction_ratio: usize,
        rng: ::rand::rngs::StdRng,
    ) -> Self {
        Self {
            accounts,
            client,
            all_addresses,
            stop,
            params,
            stats,
            txn_generator,
            invalid_transaction_ratio,
            rng,
        }
    }

    #[allow(clippy::collapsible_if)]
    pub(crate) async fn run(mut self, gas_price: u64) -> Vec<LocalAccount> {
        // Introduce a random jitter between 0 to 5 seconds so that we don't hammer the rest APIs
        // all at once.
        let random_jitter_ms = self.rng.gen_range(0, 5000);
        sleep(Duration::from_millis(random_jitter_ms)).await;

        let check_stats_at_end = self.params.check_stats_at_end && !self.params.wait_committed;
        let wait_for_accounts_sequence_timeout = Duration::from_secs(min(
            self.params.txn_expiration_time_secs,
            TXN_EXPIRATION_SECONDS,
        ));

        let wait_duration = Duration::from_millis(self.params.wait_millis);

        let start_time = Instant::now();
        let mut total_num_requests = 0;

        while !self.stop.load(Ordering::Relaxed) {
            let requests = self.gen_requests(gas_price);
            let num_requests = requests.len();
            total_num_requests += num_requests;
            let loop_start_time = Arc::new(Instant::now());
            let wait_until = *loop_start_time + wait_duration;
            let txn_offset_time = Arc::new(AtomicU64::new(0));

            if let Err(e) = try_join_all(requests.chunks(10).map(|reqs| {
                submit_transactions(
                    &self.client,
                    reqs,
                    loop_start_time.clone(),
                    txn_offset_time.clone(),
                    self.stats.clone(),
                )
            }))
            .await
            {
                sample!(
                    SampleRate::Duration(Duration::from_secs(30)),
                    warn!("[{:?}] Failed to submit request: {:?}", self.client, e)
                );
            }

            if self.params.wait_committed {
                let loop_start_time = Instant::now();
                self.update_stats(
                    loop_start_time,
                    txn_offset_time.load(Ordering::Relaxed),
                    num_requests,
                    false,
                    wait_for_accounts_sequence_timeout,
                )
                .await;
            }
            let now = Instant::now();
            if wait_until > now {
                sleep(wait_until - now).await;
            }
        }

        // If this was a burst mode run and the user didn't specifically opt
        // out of it, update the stats for the whole run.
        if check_stats_at_end {
            debug!("Checking stats for final time at the end");
            self.update_stats(
                start_time,
                0,
                total_num_requests,
                true,
                Duration::from_millis(500),
            )
            .await
        }

        self.accounts
    }

    /// This function assumes that num_requests == num_accounts, which is
    /// precisely how gen_requests works. If this changes, this code will
    /// need to be fixed.
    ///
    /// Note, the latency values are not accurate if --check-stats-at-end
    /// is used. There is no easy way around this accurately. As such, we
    /// don't update latency at all if that flag is set.
    async fn update_stats(
        &mut self,
        start_time: Instant,
        txn_offset_time: u64,
        num_requests: usize,
        skip_latency_stats: bool,
        wait_for_accounts_sequence_timeout: Duration,
    ) {
        match wait_for_accounts_sequence(
            &self.client,
            &mut self.accounts,
            wait_for_accounts_sequence_timeout,
            &mut self.rng,
        )
        .await
        {
            Ok(()) => {
                let latency = (Instant::now() - start_time).as_millis() as u64
                    - txn_offset_time / num_requests as u64;
                self.stats
                    .committed
                    .fetch_add(num_requests as u64, Ordering::Relaxed);
                if !skip_latency_stats {
                    self.stats
                        .latency
                        .fetch_add(latency * num_requests as u64, Ordering::Relaxed);
                    self.stats
                        .latencies
                        .record_data_point(latency, num_requests as u64);
                }
            }
            Err(uncommitted) => {
                let num_uncommitted = uncommitted.len() as u64;
                let num_committed = num_requests as u64 - num_uncommitted;
                // To avoid negative result caused by uncommitted tx occur
                // Simplified from:
                // end_time * num_committed - (txn_offset_time/num_requests) * num_committed
                // to
                // (end_time - txn_offset_time / num_requests) * num_committed
                let latency = (Instant::now() - start_time).as_millis() as u64
                    - txn_offset_time / num_requests as u64;
                let committed_latency = latency * num_committed as u64;
                self.stats
                    .committed
                    .fetch_add(num_committed, Ordering::Relaxed);
                self.stats
                    .expired
                    .fetch_add(num_uncommitted, Ordering::Relaxed);
                if !skip_latency_stats {
                    self.stats
                        .latency
                        .fetch_add(committed_latency, Ordering::Relaxed);
                    self.stats
                        .latencies
                        .record_data_point(latency, num_committed);
                }
                sample!(
                    SampleRate::Duration(Duration::from_secs(60)),
                    warn!(
                        "[{:?}] Transactions were not committed before expiration: {:?}",
                        self.client, uncommitted
                    )
                );
            }
        }
    }

    fn gen_requests(&mut self, gas_price: u64) -> Vec<SignedTransaction> {
        let batch_size = max(MAX_TXN_BATCH_SIZE, self.accounts.len());
        let accounts = self
            .accounts
            .iter_mut()
            .choose_multiple(&mut self.rng, batch_size);
        self.txn_generator.generate_transactions(
            accounts,
            TRANSACTIONS_PER_ACCOUNT,
            self.all_addresses.clone(),
            self.invalid_transaction_ratio,
            gas_price,
        )
    }
}

pub async fn submit_transactions(
    client: &RestClient,
    txns: &[SignedTransaction],
    loop_start_time: Arc<Instant>,
    txn_offset_time: Arc<AtomicU64>,
    stats: Arc<StatsAccumulator>,
) -> anyhow::Result<()> {
    let cur_time = Instant::now();
    let offset = cur_time - *loop_start_time;
    txn_offset_time.fetch_add(
        txns.len() as u64 * offset.as_millis() as u64,
        Ordering::Relaxed,
    );
    stats
        .submitted
        .fetch_add(txns.len() as u64, Ordering::Relaxed);
    match client.submit_batch_bcs(txns).await {
        Err(e) => sample!(
            SampleRate::Duration(Duration::from_secs(60)),
            warn!("[{:?}] Failed to submit batch request: {:?}", client, e)
        ),
        Ok(v) => {
            for f in v.into_inner().transaction_failures {
                sample!(
                    SampleRate::Duration(Duration::from_secs(60)),
                    warn!(
                        "[{:?}] Failed to submit a request within a batch: {:?}",
                        client, f
                    )
                );
            }
        }
    };
    Ok(())
}
<commit_msg>[Txn emitter] Reduce log frequency to reduce log volume for Forge (#3401)<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    emitter::{
        stats::StatsAccumulator, wait_for_accounts_sequence, MAX_TXN_BATCH_SIZE,
        TRANSACTIONS_PER_ACCOUNT, TXN_EXPIRATION_SECONDS,
    },
    transaction_generator::TransactionGenerator,
    EmitThreadParams,
};
use aptos_logger::sample::SampleRate;
use aptos_logger::sample::Sampling;
use aptos_logger::{debug, sample, warn};
use aptos_rest_client::Client as RestClient;
use aptos_sdk::{
    move_types::account_address::AccountAddress,
    types::{transaction::SignedTransaction, LocalAccount},
};
use core::{
    cmp::{max, min},
    result::Result::{Err, Ok},
    sync::atomic::{AtomicBool, Ordering},
    time::Duration,
};
use futures::future::try_join_all;
use rand::seq::IteratorRandom;
use rand::Rng;
use std::sync::atomic::AtomicU64;
use std::{sync::Arc, time::Instant};
use tokio::time::sleep;

#[derive(Debug)]
pub struct SubmissionWorker {
    pub(crate) accounts: Vec<LocalAccount>,
    client: RestClient,
    all_addresses: Arc<Vec<AccountAddress>>,
    stop: Arc<AtomicBool>,
    params: EmitThreadParams,
    stats: Arc<StatsAccumulator>,
    txn_generator: Box<dyn TransactionGenerator>,
    invalid_transaction_ratio: usize,
    rng: ::rand::rngs::StdRng,
}

// Note, there is an edge case that can occur if the transaction emitter
// bursts the target node too fast, and the emitter doesn't handle it
// very well, instead waiting up until the timeout for the target seqnum
// to progress, even though it never will. See more here:
// https://github.com/aptos-labs/aptos-core/issues/
impl SubmissionWorker {
    pub fn new(
        accounts: Vec<LocalAccount>,
        client: RestClient,
        all_addresses: Arc<Vec<AccountAddress>>,
        stop: Arc<AtomicBool>,
        params: EmitThreadParams,
        stats: Arc<StatsAccumulator>,
        txn_generator: Box<dyn TransactionGenerator>,
        invalid_transaction_ratio: usize,
        rng: ::rand::rngs::StdRng,
    ) -> Self {
        Self {
            accounts,
            client,
            all_addresses,
            stop,
            params,
            stats,
            txn_generator,
            invalid_transaction_ratio,
            rng,
        }
    }

    #[allow(clippy::collapsible_if)]
    pub(crate) async fn run(mut self, gas_price: u64) -> Vec<LocalAccount> {
        // Introduce a random jitter between 0 to 5 seconds so that we don't hammer the rest APIs
        // all at once.
        let random_jitter_ms = self.rng.gen_range(0, 5000);
        sleep(Duration::from_millis(random_jitter_ms)).await;

        let check_stats_at_end = self.params.check_stats_at_end && !self.params.wait_committed;
        let wait_for_accounts_sequence_timeout = Duration::from_secs(min(
            self.params.txn_expiration_time_secs,
            TXN_EXPIRATION_SECONDS,
        ));

        let wait_duration = Duration::from_millis(self.params.wait_millis);

        let start_time = Instant::now();
        let mut total_num_requests = 0;

        while !self.stop.load(Ordering::Relaxed) {
            let requests = self.gen_requests(gas_price);
            let num_requests = requests.len();
            total_num_requests += num_requests;
            let loop_start_time = Arc::new(Instant::now());
            let wait_until = *loop_start_time + wait_duration;
            let txn_offset_time = Arc::new(AtomicU64::new(0));

            if let Err(e) = try_join_all(requests.chunks(10).map(|reqs| {
                submit_transactions(
                    &self.client,
                    reqs,
                    loop_start_time.clone(),
                    txn_offset_time.clone(),
                    self.stats.clone(),
                )
            }))
            .await
            {
                sample!(
                    SampleRate::Duration(Duration::from_secs(120)),
                    warn!("[{:?}] Failed to submit request: {:?}", self.client, e)
                );
            }

            if self.params.wait_committed {
                let loop_start_time = Instant::now();
                self.update_stats(
                    loop_start_time,
                    txn_offset_time.load(Ordering::Relaxed),
                    num_requests,
                    false,
                    wait_for_accounts_sequence_timeout,
                )
                .await;
            }
            let now = Instant::now();
            if wait_until > now {
                sleep(wait_until - now).await;
            }
        }

        // If this was a burst mode run and the user didn't specifically opt
        // out of it, update the stats for the whole run.
        if check_stats_at_end {
            debug!("Checking stats for final time at the end");
            self.update_stats(
                start_time,
                0,
                total_num_requests,
                true,
                Duration::from_millis(500),
            )
            .await
        }

        self.accounts
    }

    /// This function assumes that num_requests == num_accounts, which is
    /// precisely how gen_requests works. If this changes, this code will
    /// need to be fixed.
    ///
    /// Note, the latency values are not accurate if --check-stats-at-end
    /// is used. There is no easy way around this accurately. As such, we
    /// don't update latency at all if that flag is set.
    async fn update_stats(
        &mut self,
        start_time: Instant,
        txn_offset_time: u64,
        num_requests: usize,
        skip_latency_stats: bool,
        wait_for_accounts_sequence_timeout: Duration,
    ) {
        match wait_for_accounts_sequence(
            &self.client,
            &mut self.accounts,
            wait_for_accounts_sequence_timeout,
            &mut self.rng,
        )
        .await
        {
            Ok(()) => {
                let latency = (Instant::now() - start_time).as_millis() as u64
                    - txn_offset_time / num_requests as u64;
                self.stats
                    .committed
                    .fetch_add(num_requests as u64, Ordering::Relaxed);
                if !skip_latency_stats {
                    self.stats
                        .latency
                        .fetch_add(latency * num_requests as u64, Ordering::Relaxed);
                    self.stats
                        .latencies
                        .record_data_point(latency, num_requests as u64);
                }
            }
            Err(uncommitted) => {
                let num_uncommitted = uncommitted.len() as u64;
                let num_committed = num_requests as u64 - num_uncommitted;
                // To avoid negative result caused by uncommitted tx occur
                // Simplified from:
                // end_time * num_committed - (txn_offset_time/num_requests) * num_committed
                // to
                // (end_time - txn_offset_time / num_requests) * num_committed
                let latency = (Instant::now() - start_time).as_millis() as u64
                    - txn_offset_time / num_requests as u64;
                let committed_latency = latency * num_committed as u64;
                self.stats
                    .committed
                    .fetch_add(num_committed, Ordering::Relaxed);
                self.stats
                    .expired
                    .fetch_add(num_uncommitted, Ordering::Relaxed);
                if !skip_latency_stats {
                    self.stats
                        .latency
                        .fetch_add(committed_latency, Ordering::Relaxed);
                    self.stats
                        .latencies
                        .record_data_point(latency, num_committed);
                }
                sample!(
                    SampleRate::Duration(Duration::from_secs(120)),
                    warn!(
                        "[{:?}] Transactions were not committed before expiration: {:?}",
                        self.client, uncommitted
                    )
                );
            }
        }
    }

    fn gen_requests(&mut self, gas_price: u64) -> Vec<SignedTransaction> {
        let batch_size = max(MAX_TXN_BATCH_SIZE, self.accounts.len());
        let accounts = self
            .accounts
            .iter_mut()
            .choose_multiple(&mut self.rng, batch_size);
        self.txn_generator.generate_transactions(
            accounts,
            TRANSACTIONS_PER_ACCOUNT,
            self.all_addresses.clone(),
            self.invalid_transaction_ratio,
            gas_price,
        )
    }
}

pub async fn submit_transactions(
    client: &RestClient,
    txns: &[SignedTransaction],
    loop_start_time: Arc<Instant>,
    txn_offset_time: Arc<AtomicU64>,
    stats: Arc<StatsAccumulator>,
) -> anyhow::Result<()> {
    let cur_time = Instant::now();
    let offset = cur_time - *loop_start_time;
    txn_offset_time.fetch_add(
        txns.len() as u64 * offset.as_millis() as u64,
        Ordering::Relaxed,
    );
    stats
        .submitted
        .fetch_add(txns.len() as u64, Ordering::Relaxed);
    match client.submit_batch_bcs(txns).await {
        Err(e) => sample!(
            SampleRate::Duration(Duration::from_secs(120)),
            warn!("[{:?}] Failed to submit batch request: {:?}", client, e)
        ),
        Ok(v) => {
            for f in v.into_inner().transaction_failures {
                sample!(
                    SampleRate::Duration(Duration::from_secs(120)),
                    warn!(
                        "[{:?}] Failed to submit a request within a batch: {:?}",
                        client, f
                    )
                );
            }
        }
    };
    Ok(())
}
<|endoftext|>


<commit_before>extern crate libudis86_sys;
extern crate regex;

extern crate unwind_mc;

mod analysis_helper;
mod gcc_tools;
mod source_tester;

use source_tester::*;

#[test]
fn add() {
    let code = "
        int add(int a, int b) {
          return a + b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 + arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}
<commit_msg>Add the remaining arithmetic tests<commit_after>extern crate libudis86_sys;
extern crate regex;

extern crate unwind_mc;

mod analysis_helper;
mod gcc_tools;
mod source_tester;

use source_tester::*;

#[test]
fn add() {
    let code = "
        int add(int a, int b) {
          return a + b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 + arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn subtract() {
    let code = "
        int subtract(int a, int b) {
          return a - b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg0;
          var0 = var0 - arg1;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn negate() {
    let code = "
        int negate(int a) {
          return -a;
        }";
    let expected = "
        int sub_000000(int arg0)
        {
          int var0;

          var0 = arg0;
          var0 = -var0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn multiply() {
    let code = "
        int multiply(int a, int b) {
          return a * b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 * arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn divide() {
    let code = "
        int divide(int a, int b) {
          return a / b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg0;
          var0 = var0 / arg1;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

// #[test]
// TODO: support multiple-output instructions
fn modulo() {
    let code = "
        int divide(int a, int b) {
          return a % b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg0;
          var0 = var0 % arg1;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn not() {
    let code = "
        int _not(int a) {
          return ~a;
        }";
    let expected = "
        int sub_000000(int arg0)
        {
          int var0;

          var0 = arg0;
          var0 = ~var0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn and() {
    let code = "
        int _and(int a, int b) {
          return a & b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 & arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn or() {
    let code = "
        int _or(int a, int b) {
          return a | b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 | arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

#[test]
fn xor() {
    let code = "
        int _xor(int a, int b) {
          return a ^ b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg1;
          var0 = var0 ^ arg0;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

// #[test]
// TODO: support word registers
fn left_shift() {
    let code = "
        int xor(int a, int b) {
          return a << b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg0;
          var0 = var0 << arg1;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}

// #[test]
// TODO: support word registers
fn right_shift() {
    let code = "
        int xor(int a, int b) {
          return a >> b;
        }";
    let expected = "
        int sub_000000(int arg0, int arg1)
        {
          int var0;

          var0 = arg0;
          var0 = var0 >> arg1;
          return var0;
        }
        ";
    test_decompiler(code, expected);
}
<|endoftext|>


<commit_before>//ignore-windows: env var emulation not implemented on Windows
//ignore-macos: env var emulation not implemented on macOS

use std::env;

fn main() {
    assert_eq!(env::var("MIRI_TEST"), Err(env::VarError::NotPresent));
    env::set_var("MIRI_TEST", "the answer");
    assert_eq!(env::var("MIRI_TEST"), Ok("the answer".to_owned()));
}
<commit_msg>env vars should work on macOS<commit_after>//ignore-windows: env var emulation not implemented on Windows

use std::env;

fn main() {
    assert_eq!(env::var("MIRI_TEST"), Err(env::VarError::NotPresent));
    env::set_var("MIRI_TEST", "the answer");
    assert_eq!(env::var("MIRI_TEST"), Ok("the answer".to_owned()));
}
<|endoftext|>


<commit_before>
extern crate rand;
use rand::Rng;

fn one_in(n: f64) -> bool {
    rand::thread_rng().gen_bool(n)
}

struct File {
    name: String,

}
fn main() {
    println!("Hello, world!");
}
<commit_msg>method impl for File struct<commit_after>#![allow(dead_code)]

extern crate rand;
use rand::Rng;

fn one_in(n: f64) -> bool {
    rand::thread_rng().gen_bool(n)
}

struct File {
    name: String,
    data: Vec<u8>,
}

impl File {
    fn new(name: &str) -> File {
        File {
            name: String::from(name),
            data: Vec::new(),
        }
    }

    fn new_with_data(name: &str, data: &Vec<u8>) -> File {
        let mut f = File::new(name);
        f.data = data.clone();
        f
    }

    fn read(self: &File, save_to: &mut Vec<u8>) -> Result<usize, String> {
        let mut tmp = self.data.clone();
        let read_length = tmp.len();
        save_to.reserve(read_length);
        save_to.append(&mut tmp);
        Ok(read_length)
    }
}

fn main() {
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>use super::frontend_prelude::*;

use crate::controllers::helpers::pagination::{Page, PaginationOptions};
use crate::controllers::util::AuthenticatedUser;
use crate::models::{Crate, CrateOwnerInvitation, Rights, User};
use crate::schema::{crate_owner_invitations, crates, users};
use crate::util::errors::{forbidden, internal};
use crate::views::{
    EncodableCrateOwnerInvitation, EncodableCrateOwnerInvitationV1, EncodablePublicUser,
    InvitationResponse,
};
use chrono::{Duration, Utc};
use diesel::{pg::Pg, sql_types::Bool};
use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};

/// Handles the `GET /api/v1/me/crate_owner_invitations` route.
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
    let auth = req.authenticate()?.forbid_api_token_auth()?;
    let user_id = auth.user_id();

    let PrivateListResponse {
        invitations, users, ..
    } = prepare_list(req, auth, ListFilter::InviteeId(user_id))?;

    // The schema for the private endpoints is converted to the schema used by v1 endpoints.
    let crate_owner_invitations = invitations
        .into_iter()
        .map(|private| {
            Ok(EncodableCrateOwnerInvitationV1 {
                invited_by_username: users
                    .iter()
                    .find(|u| u.id == private.inviter_id)
                    .ok_or_else(|| internal(&format!("missing user {}", private.inviter_id)))?
                    .login
                    .clone(),
                invitee_id: private.invitee_id,
                inviter_id: private.inviter_id,
                crate_name: private.crate_name,
                crate_id: private.crate_id,
                created_at: private.created_at,
                expires_at: private.expires_at,
            })
        })
        .collect::<AppResult<Vec<EncodableCrateOwnerInvitationV1>>>()?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitations: Vec<EncodableCrateOwnerInvitationV1>,
        users: Vec<EncodablePublicUser>,
    }
    Ok(req.json(&R {
        crate_owner_invitations,
        users,
    }))
}

/// Handles the `GET /api/private/crate_owner_invitations` route.
pub fn private_list(req: &mut dyn RequestExt) -> EndpointResult {
    let auth = req.authenticate()?.forbid_api_token_auth()?;

    let filter = if let Some(crate_name) = req.query().get("crate_name") {
        ListFilter::CrateName(crate_name.clone())
    } else if let Some(id) = req.query().get("invitee_id").and_then(|i| i.parse().ok()) {
        ListFilter::InviteeId(id)
    } else {
        return Err(bad_request("missing or invalid filter"));
    };

    let list = prepare_list(req, auth, filter)?;
    Ok(req.json(&list))
}

enum ListFilter {
    CrateName(String),
    InviteeId(i32),
}

fn prepare_list(
    req: &mut dyn RequestExt,
    auth: AuthenticatedUser,
    filter: ListFilter,
) -> AppResult<PrivateListResponse> {
    let pagination: PaginationOptions = PaginationOptions::builder()
        .enable_pages(false)
        .enable_seek(true)
        .gather(req)?;

    let user = auth.user();
    let conn = req.db_read_only()?;
    let config = &req.app().config;

    let mut crate_names = HashMap::new();
    let mut users = IndexMap::new();
    users.insert(user.id, user.clone());

    let sql_filter: Box<dyn BoxableExpression<crate_owner_invitations::table, Pg, SqlType = Bool>> =
        match filter {
            ListFilter::CrateName(crate_name) => {
                // Only allow crate owners to query pending invitations for their crate.
                let krate: Crate = Crate::by_name(&crate_name).first(&*conn)?;
                let owners = krate.owners(&*conn)?;
                if user.rights(req.app(), &owners)? != Rights::Full {
                    return Err(forbidden());
                }

                // Cache the crate name to avoid querying it from the database again
                crate_names.insert(krate.id, krate.name.clone());

                Box::new(crate_owner_invitations::crate_id.eq(krate.id))
            }
            ListFilter::InviteeId(invitee_id) => {
                if invitee_id != user.id {
                    return Err(forbidden());
                }
                Box::new(crate_owner_invitations::invited_user_id.eq(invitee_id))
            }
        };

    // Load all the non-expired invitations matching the filter.
    let expire_cutoff = Duration::days(config.ownership_invitations_expiration_days as i64);
    let query = crate_owner_invitations::table
        .filter(sql_filter)
        .filter(crate_owner_invitations::created_at.gt((Utc::now() - expire_cutoff).naive_utc()))
        .order_by((
            crate_owner_invitations::crate_id,
            crate_owner_invitations::invited_user_id,
        ))
        // We fetch one element over the page limit to then detect whether there is a next page.
        .limit(pagination.per_page as i64 + 1);

    // Load and paginate the results.
    let mut raw_invitations: Vec<CrateOwnerInvitation> = match pagination.page {
        Page::Unspecified => query.load(&*conn)?,
        Page::Seek(s) => {
            let seek_key: (i32, i32) = s.decode()?;
            query
                .filter(
                    crate_owner_invitations::crate_id.gt(seek_key.0).or(
                        crate_owner_invitations::crate_id
                            .eq(seek_key.0)
                            .and(crate_owner_invitations::invited_user_id.gt(seek_key.1)),
                    ),
                )
                .load(&*conn)?
        }
        Page::Numeric(_) => unreachable!("page-based pagination is disabled"),
    };
    let next_page = if raw_invitations.len() > pagination.per_page as usize {
        // We fetch `per_page + 1` to check if there are records for the next page. Since the last
        // element is not what the user wanted it's discarded.
        raw_invitations.pop();

        if let Some(last) = raw_invitations.last() {
            let mut params = IndexMap::new();
            params.insert(
                "seek".into(),
                crate::controllers::helpers::pagination::encode_seek((
                    last.crate_id,
                    last.invited_user_id,
                ))?,
            );
            Some(req.query_with_params(params))
        } else {
            None
        }
    } else {
        None
    };

    // Load all the related crates.
    let missing_crate_names = raw_invitations
        .iter()
        .map(|i| i.crate_id)
        .filter(|id| !crate_names.contains_key(id))
        .collect::<Vec<_>>();
    if !missing_crate_names.is_empty() {
        let new_names: Vec<(i32, String)> = crates::table
            .select((crates::id, crates::name))
            .filter(crates::id.eq_any(missing_crate_names))
            .load(&*conn)?;
        for (id, name) in new_names.into_iter() {
            crate_names.insert(id, name);
        }
    }

    // Load all the related users.
    let missing_users = raw_invitations
        .iter()
        .flat_map(|invite| {
            std::iter::once(invite.invited_user_id)
                .chain(std::iter::once(invite.invited_by_user_id))
        })
        .filter(|id| !users.contains_key(id))
        .collect::<Vec<_>>();
    if !missing_users.is_empty() {
        let new_users: Vec<User> = users::table
            .filter(users::id.eq_any(missing_users))
            .load(&*conn)?;
        for user in new_users.into_iter() {
            users.insert(user.id, user);
        }
    }

    // Turn `CrateOwnerInvitation`s into `EncodablePrivateCrateOwnerInvitation`.
    let config = &req.app().config;
    let mut invitations = Vec::new();
    let mut users_in_response = HashSet::new();
    for invitation in raw_invitations.into_iter() {
        invitations.push(EncodableCrateOwnerInvitation {
            invitee_id: invitation.invited_user_id,
            inviter_id: invitation.invited_by_user_id,
            crate_id: invitation.crate_id,
            crate_name: crate_names
                .get(&invitation.crate_id)
                .ok_or_else(|| internal(&format!("missing crate with id {}", invitation.crate_id)))?
                .clone(),
            created_at: invitation.created_at,
            expires_at: invitation.expires_at(config),
        });
        users_in_response.insert(invitation.invited_user_id);
        users_in_response.insert(invitation.invited_by_user_id);
    }

    // Provide a stable response for the users list, only including the referenced users with
    // stable sorting.
    users.retain(|k, _| users_in_response.contains(k));
    users.sort_keys();

    Ok(PrivateListResponse {
        invitations,
        users: users.into_iter().map(|(_, user)| user.into()).collect(),
        meta: ResponseMeta { next_page },
    })
}

#[derive(Serialize)]
struct PrivateListResponse {
    invitations: Vec<EncodableCrateOwnerInvitation>,
    users: Vec<EncodablePublicUser>,
    meta: ResponseMeta,
}

#[derive(Serialize)]
struct ResponseMeta {
    next_page: Option<String>,
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut dyn RequestExt) -> EndpointResult {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;
    let user_id = req.authenticate()?.user_id();
    let conn = &*req.db_conn()?;
    let config = &req.app().config;

    let invitation = CrateOwnerInvitation::find_by_id(user_id, crate_invite.crate_id, conn)?;
    if crate_invite.accepted {
        invitation.accept(conn, config)?;
    } else {
        invitation.decline(conn)?;
    }

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }
    Ok(req.json(&R {
        crate_owner_invitation: crate_invite,
    }))
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/accept/:token` route.
pub fn handle_invite_with_token(req: &mut dyn RequestExt) -> EndpointResult {
    let config = &req.app().config;
    let conn = req.db_conn()?;
    let req_token = &req.params()["token"];

    let invitation = CrateOwnerInvitation::find_by_token(req_token, &conn)?;
    let crate_id = invitation.crate_id;
    invitation.accept(&conn, config)?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }
    Ok(req.json(&R {
        crate_owner_invitation: InvitationResponse {
            crate_id,
            accepted: true,
        },
    }))
}
<commit_msg>controllers::crate_owner_invitation: Use `json!` macro to simplify JSON serialization code<commit_after>use super::frontend_prelude::*;

use crate::controllers::helpers::pagination::{Page, PaginationOptions};
use crate::controllers::util::AuthenticatedUser;
use crate::models::{Crate, CrateOwnerInvitation, Rights, User};
use crate::schema::{crate_owner_invitations, crates, users};
use crate::util::errors::{forbidden, internal};
use crate::views::{
    EncodableCrateOwnerInvitation, EncodableCrateOwnerInvitationV1, EncodablePublicUser,
    InvitationResponse,
};
use chrono::{Duration, Utc};
use diesel::{pg::Pg, sql_types::Bool};
use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};

/// Handles the `GET /api/v1/me/crate_owner_invitations` route.
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
    let auth = req.authenticate()?.forbid_api_token_auth()?;
    let user_id = auth.user_id();

    let PrivateListResponse {
        invitations, users, ..
    } = prepare_list(req, auth, ListFilter::InviteeId(user_id))?;

    // The schema for the private endpoints is converted to the schema used by v1 endpoints.
    let crate_owner_invitations = invitations
        .into_iter()
        .map(|private| {
            Ok(EncodableCrateOwnerInvitationV1 {
                invited_by_username: users
                    .iter()
                    .find(|u| u.id == private.inviter_id)
                    .ok_or_else(|| internal(&format!("missing user {}", private.inviter_id)))?
                    .login
                    .clone(),
                invitee_id: private.invitee_id,
                inviter_id: private.inviter_id,
                crate_name: private.crate_name,
                crate_id: private.crate_id,
                created_at: private.created_at,
                expires_at: private.expires_at,
            })
        })
        .collect::<AppResult<Vec<EncodableCrateOwnerInvitationV1>>>()?;

    Ok(req.json(&json!({
        "crate_owner_invitations": crate_owner_invitations,
        "users": users,
    })))
}

/// Handles the `GET /api/private/crate_owner_invitations` route.
pub fn private_list(req: &mut dyn RequestExt) -> EndpointResult {
    let auth = req.authenticate()?.forbid_api_token_auth()?;

    let filter = if let Some(crate_name) = req.query().get("crate_name") {
        ListFilter::CrateName(crate_name.clone())
    } else if let Some(id) = req.query().get("invitee_id").and_then(|i| i.parse().ok()) {
        ListFilter::InviteeId(id)
    } else {
        return Err(bad_request("missing or invalid filter"));
    };

    let list = prepare_list(req, auth, filter)?;
    Ok(req.json(&list))
}

enum ListFilter {
    CrateName(String),
    InviteeId(i32),
}

fn prepare_list(
    req: &mut dyn RequestExt,
    auth: AuthenticatedUser,
    filter: ListFilter,
) -> AppResult<PrivateListResponse> {
    let pagination: PaginationOptions = PaginationOptions::builder()
        .enable_pages(false)
        .enable_seek(true)
        .gather(req)?;

    let user = auth.user();
    let conn = req.db_read_only()?;
    let config = &req.app().config;

    let mut crate_names = HashMap::new();
    let mut users = IndexMap::new();
    users.insert(user.id, user.clone());

    let sql_filter: Box<dyn BoxableExpression<crate_owner_invitations::table, Pg, SqlType = Bool>> =
        match filter {
            ListFilter::CrateName(crate_name) => {
                // Only allow crate owners to query pending invitations for their crate.
                let krate: Crate = Crate::by_name(&crate_name).first(&*conn)?;
                let owners = krate.owners(&*conn)?;
                if user.rights(req.app(), &owners)? != Rights::Full {
                    return Err(forbidden());
                }

                // Cache the crate name to avoid querying it from the database again
                crate_names.insert(krate.id, krate.name.clone());

                Box::new(crate_owner_invitations::crate_id.eq(krate.id))
            }
            ListFilter::InviteeId(invitee_id) => {
                if invitee_id != user.id {
                    return Err(forbidden());
                }
                Box::new(crate_owner_invitations::invited_user_id.eq(invitee_id))
            }
        };

    // Load all the non-expired invitations matching the filter.
    let expire_cutoff = Duration::days(config.ownership_invitations_expiration_days as i64);
    let query = crate_owner_invitations::table
        .filter(sql_filter)
        .filter(crate_owner_invitations::created_at.gt((Utc::now() - expire_cutoff).naive_utc()))
        .order_by((
            crate_owner_invitations::crate_id,
            crate_owner_invitations::invited_user_id,
        ))
        // We fetch one element over the page limit to then detect whether there is a next page.
        .limit(pagination.per_page as i64 + 1);

    // Load and paginate the results.
    let mut raw_invitations: Vec<CrateOwnerInvitation> = match pagination.page {
        Page::Unspecified => query.load(&*conn)?,
        Page::Seek(s) => {
            let seek_key: (i32, i32) = s.decode()?;
            query
                .filter(
                    crate_owner_invitations::crate_id.gt(seek_key.0).or(
                        crate_owner_invitations::crate_id
                            .eq(seek_key.0)
                            .and(crate_owner_invitations::invited_user_id.gt(seek_key.1)),
                    ),
                )
                .load(&*conn)?
        }
        Page::Numeric(_) => unreachable!("page-based pagination is disabled"),
    };
    let next_page = if raw_invitations.len() > pagination.per_page as usize {
        // We fetch `per_page + 1` to check if there are records for the next page. Since the last
        // element is not what the user wanted it's discarded.
        raw_invitations.pop();

        if let Some(last) = raw_invitations.last() {
            let mut params = IndexMap::new();
            params.insert(
                "seek".into(),
                crate::controllers::helpers::pagination::encode_seek((
                    last.crate_id,
                    last.invited_user_id,
                ))?,
            );
            Some(req.query_with_params(params))
        } else {
            None
        }
    } else {
        None
    };

    // Load all the related crates.
    let missing_crate_names = raw_invitations
        .iter()
        .map(|i| i.crate_id)
        .filter(|id| !crate_names.contains_key(id))
        .collect::<Vec<_>>();
    if !missing_crate_names.is_empty() {
        let new_names: Vec<(i32, String)> = crates::table
            .select((crates::id, crates::name))
            .filter(crates::id.eq_any(missing_crate_names))
            .load(&*conn)?;
        for (id, name) in new_names.into_iter() {
            crate_names.insert(id, name);
        }
    }

    // Load all the related users.
    let missing_users = raw_invitations
        .iter()
        .flat_map(|invite| {
            std::iter::once(invite.invited_user_id)
                .chain(std::iter::once(invite.invited_by_user_id))
        })
        .filter(|id| !users.contains_key(id))
        .collect::<Vec<_>>();
    if !missing_users.is_empty() {
        let new_users: Vec<User> = users::table
            .filter(users::id.eq_any(missing_users))
            .load(&*conn)?;
        for user in new_users.into_iter() {
            users.insert(user.id, user);
        }
    }

    // Turn `CrateOwnerInvitation`s into `EncodablePrivateCrateOwnerInvitation`.
    let config = &req.app().config;
    let mut invitations = Vec::new();
    let mut users_in_response = HashSet::new();
    for invitation in raw_invitations.into_iter() {
        invitations.push(EncodableCrateOwnerInvitation {
            invitee_id: invitation.invited_user_id,
            inviter_id: invitation.invited_by_user_id,
            crate_id: invitation.crate_id,
            crate_name: crate_names
                .get(&invitation.crate_id)
                .ok_or_else(|| internal(&format!("missing crate with id {}", invitation.crate_id)))?
                .clone(),
            created_at: invitation.created_at,
            expires_at: invitation.expires_at(config),
        });
        users_in_response.insert(invitation.invited_user_id);
        users_in_response.insert(invitation.invited_by_user_id);
    }

    // Provide a stable response for the users list, only including the referenced users with
    // stable sorting.
    users.retain(|k, _| users_in_response.contains(k));
    users.sort_keys();

    Ok(PrivateListResponse {
        invitations,
        users: users.into_iter().map(|(_, user)| user.into()).collect(),
        meta: ResponseMeta { next_page },
    })
}

#[derive(Serialize)]
struct PrivateListResponse {
    invitations: Vec<EncodableCrateOwnerInvitation>,
    users: Vec<EncodablePublicUser>,
    meta: ResponseMeta,
}

#[derive(Serialize)]
struct ResponseMeta {
    next_page: Option<String>,
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut dyn RequestExt) -> EndpointResult {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;
    let user_id = req.authenticate()?.user_id();
    let conn = &*req.db_conn()?;
    let config = &req.app().config;

    let invitation = CrateOwnerInvitation::find_by_id(user_id, crate_invite.crate_id, conn)?;
    if crate_invite.accepted {
        invitation.accept(conn, config)?;
    } else {
        invitation.decline(conn)?;
    }

    Ok(req.json(&json!({ "crate_owner_invitation": crate_invite })))
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/accept/:token` route.
pub fn handle_invite_with_token(req: &mut dyn RequestExt) -> EndpointResult {
    let config = &req.app().config;
    let conn = req.db_conn()?;
    let req_token = &req.params()["token"];

    let invitation = CrateOwnerInvitation::find_by_token(req_token, &conn)?;
    let crate_id = invitation.crate_id;
    invitation.accept(&conn, config)?;

    Ok(req.json(&json!({
        "crate_owner_invitation": {
            "crate_id": crate_id,
            "accepted": true,
        },
    })))
}
<|endoftext|>