<commit_before>use std::{
    ffi::CString,
    fmt::Debug,
    sync::{Arc, Mutex},
};

use wayland_protocols_misc::zwp_virtual_keyboard_v1::server::zwp_virtual_keyboard_v1::{
    self, ZwpVirtualKeyboardV1,
};
use wayland_server::{
    backend::{ClientId, ObjectId},
    protocol::wl_keyboard::{KeyState, KeymapFormat},
    Client, DataInit, Dispatch, DisplayHandle,
};
use xkbcommon::xkb;

use crate::{
    input::{keyboard::KeymapFile, Seat, SeatHandler},
    utils::SERIAL_COUNTER,
    wayland::seat::{keyboard::for_each_focused_kbds, WaylandFocus},
};

use super::VirtualKeyboardManagerState;

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
struct SerializedMods {
    depressed: u32,
    latched: u32,
    locked: u32,
    group: u32,
}

#[derive(Debug, Default)]
pub(crate) struct VirtualKeyboard {
    instances: u8,
    modifiers: Option<SerializedMods>,
    old_keymap: Option<KeymapFile>,
}

/// Handle to a virtual keyboard instance
#[derive(Debug, Clone, Default)]
pub(crate) struct VirtualKeyboardHandle {
    pub(crate) inner: Arc<Mutex<VirtualKeyboard>>,
}

impl VirtualKeyboardHandle {
    pub(super) fn count_instance(&self) {
        let mut inner = self.inner.lock().unwrap();
        inner.instances += 1;
    }
}

/// User data of ZwpVirtualKeyboardV1 object
pub struct VirtualKeyboardUserData<D: SeatHandler> {
    pub(super) handle: VirtualKeyboardHandle,
    pub(crate) seat: Seat<D>,
}

impl<D: SeatHandler> Debug for VirtualKeyboardUserData<D>
where
    <D as SeatHandler>::KeyboardFocus: Debug,
    <D as SeatHandler>::PointerFocus: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("VirtualKeyboardUserData")
            .field("handle", &self.handle)
            .field("seat", &self.seat.arc)
            .finish()
    }
}

impl<D> Dispatch<ZwpVirtualKeyboardV1, VirtualKeyboardUserData<D>, D> for VirtualKeyboardManagerState
where
    D: Dispatch<ZwpVirtualKeyboardV1, VirtualKeyboardUserData<D>>,
    D: SeatHandler + 'static,
    <D as SeatHandler>::KeyboardFocus: WaylandFocus,
{
    fn request(
        _data: &mut D,
        _client: &Client,
        _: &ZwpVirtualKeyboardV1,
        request: zwp_virtual_keyboard_v1::Request,
        data: &VirtualKeyboardUserData<D>,
        _dh: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwp_virtual_keyboard_v1::Request::Keymap { format, fd, size } => {
                // This should be wl_keyboard::KeymapFormat::XkbV1,
                // but the protocol does not state the parameter is an enum.
                if format == 1 {
                    let keyboard_handle = data.seat.get_keyboard().unwrap();
                    let mut internal = keyboard_handle.arc.internal.lock().unwrap();
                    let old_keymap = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
                    let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
                    let new_keymap = unsafe {
                        xkb::Keymap::new_from_fd(
                            &context,
                            fd,
                            size as usize,
                            format,
                            xkb::KEYMAP_COMPILE_NO_FLAGS,
                        )
                        .unwrap()
                        .unwrap()
                    };
                    if old_keymap != new_keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1) {
                        let mut inner = data.handle.inner.lock().unwrap();
                        let log = crate::slog_or_fallback(None);
                        if inner.old_keymap.is_none() {
                            let old_keymap = CString::new(old_keymap)
                                .expect("Keymap should not contain interior null bytes");
                            inner.old_keymap = Some(KeymapFile::new(old_keymap, log));
                        }
                        internal.keymap = new_keymap;
                        let known_kbds = &keyboard_handle.arc.known_kbds;
                        for kbd in &*known_kbds.lock().unwrap() {
                            kbd.keymap(KeymapFormat::XkbV1, fd, size);
                        }
                    }
                }
            }
            zwp_virtual_keyboard_v1::Request::Key { time, key, state } => {
                let keyboard_handle = data.seat.get_keyboard().unwrap();
                let internal = keyboard_handle.arc.internal.lock().unwrap();
                let inner = data.handle.inner.lock().unwrap();
                if let Some(focus) = internal.focus.as_ref().and_then(|f| f.0.wl_surface()) {
                    for_each_focused_kbds(&data.seat, focus, |kbd| {
                        // This should be wl_keyboard::KeyState,
                        // but the protocol does not state the parameter is an enum.
                        let key_state = if state == 1 {
                            KeyState::Pressed
                        } else {
                            KeyState::Released
                        };
                        kbd.key(SERIAL_COUNTER.next_serial().0, time, key, key_state);
                        if let Some(mods) = inner.modifiers {
                            kbd.modifiers(
                                SERIAL_COUNTER.next_serial().0,
                                mods.depressed,
                                mods.latched,
                                mods.locked,
                                mods.group,
                            );
                        }
                    });
                }
            }
            zwp_virtual_keyboard_v1::Request::Modifiers {
                mods_depressed,
                mods_latched,
                mods_locked,
                group,
            } => {
                data.handle.inner.lock().unwrap().modifiers = Some(SerializedMods {
                    depressed: mods_depressed,
                    latched: mods_latched,
                    locked: mods_locked,
                    group,
                });
            }
            zwp_virtual_keyboard_v1::Request::Destroy => {
                // Nothing to do
            }
            _ => unreachable!(),
        }
    }

    fn destroyed(
        _state: &mut D,
        _client: ClientId,
        _virtual_keyboard: ObjectId,
        data: &VirtualKeyboardUserData<D>,
    ) {
        let mut inner = data.handle.inner.lock().unwrap();
        inner.instances -= 1;
        if inner.instances == 0 {
            if let Some(old_keymap) = &inner.old_keymap {
                old_keymap
                    .with_fd(false, |fd, size| {
                        let keyboard_handle = data.seat.get_keyboard().unwrap();
                        let known_kbds = &keyboard_handle.arc.known_kbds;
                        for kbd in &*known_kbds.lock().unwrap() {
                            kbd.keymap(KeymapFormat::XkbV1, fd, size as _);
                        }
                        let mut internal = keyboard_handle.arc.internal.lock().unwrap();
                        let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
                        internal.keymap = unsafe {
                            xkb::Keymap::new_from_fd(
                                &context,
                                fd,
                                size,
                                KeymapFormat::XkbV1.into(),
                                xkb::KEYMAP_COMPILE_NO_FLAGS,
                            )
                            .unwrap()
                            .unwrap()
                        }
                    })
                    .unwrap(); //TODO: log some kind of error here;
                inner.old_keymap = None;
            }
        }
    }
}
<commit_msg>convert OwnedFd to raw for xkbcommon<commit_after>use std::{
    ffi::CString,
    fmt::Debug,
    os::unix::prelude::AsRawFd,
    sync::{Arc, Mutex},
};

use wayland_protocols_misc::zwp_virtual_keyboard_v1::server::zwp_virtual_keyboard_v1::{
    self, ZwpVirtualKeyboardV1,
};
use wayland_server::{
    backend::{ClientId, ObjectId},
    protocol::wl_keyboard::{KeyState, KeymapFormat},
    Client, DataInit, Dispatch, DisplayHandle,
};
use xkbcommon::xkb;

use crate::{
    input::{keyboard::KeymapFile, Seat, SeatHandler},
    utils::SERIAL_COUNTER,
    wayland::seat::{keyboard::for_each_focused_kbds, WaylandFocus},
};

use super::VirtualKeyboardManagerState;

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
struct SerializedMods {
    depressed: u32,
    latched: u32,
    locked: u32,
    group: u32,
}

#[derive(Debug, Default)]
pub(crate) struct VirtualKeyboard {
    instances: u8,
    modifiers: Option<SerializedMods>,
    old_keymap: Option<KeymapFile>,
}

/// Handle to a virtual keyboard instance
#[derive(Debug, Clone, Default)]
pub(crate) struct VirtualKeyboardHandle {
    pub(crate) inner: Arc<Mutex<VirtualKeyboard>>,
}

impl VirtualKeyboardHandle {
    pub(super) fn count_instance(&self) {
        let mut inner = self.inner.lock().unwrap();
        inner.instances += 1;
    }
}

/// User data of ZwpVirtualKeyboardV1 object
pub struct VirtualKeyboardUserData<D: SeatHandler> {
    pub(super) handle: VirtualKeyboardHandle,
    pub(crate) seat: Seat<D>,
}

impl<D: SeatHandler> Debug for VirtualKeyboardUserData<D>
where
    <D as SeatHandler>::KeyboardFocus: Debug,
    <D as SeatHandler>::PointerFocus: Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("VirtualKeyboardUserData")
            .field("handle", &self.handle)
            .field("seat", &self.seat.arc)
            .finish()
    }
}

impl<D> Dispatch<ZwpVirtualKeyboardV1, VirtualKeyboardUserData<D>, D> for VirtualKeyboardManagerState
where
    D: Dispatch<ZwpVirtualKeyboardV1, VirtualKeyboardUserData<D>>,
    D: SeatHandler + 'static,
    <D as SeatHandler>::KeyboardFocus: WaylandFocus,
{
    fn request(
        _data: &mut D,
        _client: &Client,
        _: &ZwpVirtualKeyboardV1,
        request: zwp_virtual_keyboard_v1::Request,
        data: &VirtualKeyboardUserData<D>,
        _dh: &DisplayHandle,
        _data_init: &mut DataInit<'_, D>,
    ) {
        match request {
            zwp_virtual_keyboard_v1::Request::Keymap { format, fd, size } => {
                // This should be wl_keyboard::KeymapFormat::XkbV1,
                // but the protocol does not state the parameter is an enum.
                if format == 1 {
                    let keyboard_handle = data.seat.get_keyboard().unwrap();
                    let mut internal = keyboard_handle.arc.internal.lock().unwrap();
                    let old_keymap = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
                    let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
                    let new_keymap = unsafe {
                        xkb::Keymap::new_from_fd(
                            &context,
                            fd.as_raw_fd(),
                            size as usize,
                            format,
                            xkb::KEYMAP_COMPILE_NO_FLAGS,
                        )
                        .unwrap()
                        .unwrap()
                    };
                    if old_keymap != new_keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1) {
                        let mut inner = data.handle.inner.lock().unwrap();
                        let log = crate::slog_or_fallback(None);
                        if inner.old_keymap.is_none() {
                            let old_keymap = CString::new(old_keymap)
                                .expect("Keymap should not contain interior null bytes");
                            inner.old_keymap = Some(KeymapFile::new(old_keymap, log));
                        }
                        internal.keymap = new_keymap;
                        let known_kbds = &keyboard_handle.arc.known_kbds;
                        for kbd in &*known_kbds.lock().unwrap() {
                            kbd.keymap(KeymapFormat::XkbV1, fd.as_raw_fd(), size);
                        }
                    }
                }
            }
            zwp_virtual_keyboard_v1::Request::Key { time, key, state } => {
                let keyboard_handle = data.seat.get_keyboard().unwrap();
                let internal = keyboard_handle.arc.internal.lock().unwrap();
                let inner = data.handle.inner.lock().unwrap();
                if let Some(focus) = internal.focus.as_ref().and_then(|f| f.0.wl_surface()) {
                    for_each_focused_kbds(&data.seat, focus, |kbd| {
                        // This should be wl_keyboard::KeyState,
                        // but the protocol does not state the parameter is an enum.
                        let key_state = if state == 1 {
                            KeyState::Pressed
                        } else {
                            KeyState::Released
                        };
                        kbd.key(SERIAL_COUNTER.next_serial().0, time, key, key_state);
                        if let Some(mods) = inner.modifiers {
                            kbd.modifiers(
                                SERIAL_COUNTER.next_serial().0,
                                mods.depressed,
                                mods.latched,
                                mods.locked,
                                mods.group,
                            );
                        }
                    });
                }
            }
            zwp_virtual_keyboard_v1::Request::Modifiers {
                mods_depressed,
                mods_latched,
                mods_locked,
                group,
            } => {
                data.handle.inner.lock().unwrap().modifiers = Some(SerializedMods {
                    depressed: mods_depressed,
                    latched: mods_latched,
                    locked: mods_locked,
                    group,
                });
            }
            zwp_virtual_keyboard_v1::Request::Destroy => {
                // Nothing to do
            }
            _ => unreachable!(),
        }
    }

    fn destroyed(
        _state: &mut D,
        _client: ClientId,
        _virtual_keyboard: ObjectId,
        data: &VirtualKeyboardUserData<D>,
    ) {
        let mut inner = data.handle.inner.lock().unwrap();
        inner.instances -= 1;
        if inner.instances == 0 {
            if let Some(old_keymap) = &inner.old_keymap {
                old_keymap
                    .with_fd(false, |fd, size| {
                        let keyboard_handle = data.seat.get_keyboard().unwrap();
                        let known_kbds = &keyboard_handle.arc.known_kbds;
                        for kbd in &*known_kbds.lock().unwrap() {
                            kbd.keymap(KeymapFormat::XkbV1, fd, size as _);
                        }
                        let mut internal = keyboard_handle.arc.internal.lock().unwrap();
                        let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
                        internal.keymap = unsafe {
                            xkb::Keymap::new_from_fd(
                                &context,
                                fd,
                                size,
                                KeymapFormat::XkbV1.into(),
                                xkb::KEYMAP_COMPILE_NO_FLAGS,
                            )
                            .unwrap()
                            .unwrap()
                        }
                    })
                    .unwrap(); //TODO: log some kind of error here;
                inner.old_keymap = None;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate hyper;
extern crate tools;

use std::collections::HashMap;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Mutex;

use hyper::header::ContentType;
use hyper::method::Method;
use hyper::mime::Mime;
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri;

use tools::blame;
use tools::config;
use tools::file_format::identifiers::IdentMap;
use tools::format;

struct WebRequest {
    path: String,
}

struct WebResponse {
    status: StatusCode,
    content_type: String,
    output: String,
}

fn not_found() -> WebResponse {
    WebResponse {
        status: StatusCode::NotFound,
        content_type: "text/plain".to_owned(),
        output: "Not found".to_owned(),
    }
}

fn handle_static(path: String, content_type: Option<&str>) -> WebResponse {
    let source_file = match File::open(&path) {
        Ok(f) => f,
        Err(_) => {
            return not_found();
        }
    };
    let mut reader = BufReader::new(&source_file);
    let mut input = String::new();
    match reader.read_to_string(&mut input) {
        Ok(_) => {}
        Err(_) => {
            return not_found();
        }
    }

    let inferred_content_type = match Path::new(&path).extension() {
        Some(ext) => match ext.to_str().unwrap() {
            "css" => "text/css",
            "js" => "text/javascript",
            _ => "text/html",
        },
        None => "text/html",
    };
    let content_type = match content_type {
        Some(ct) => ct,
        None => inferred_content_type,
    };

    WebResponse {
        status: StatusCode::Ok,
        content_type: content_type.to_owned(),
        output: input,
    }
}

fn handle(
    cfg: &config::Config,
    ident_map: &HashMap<String, IdentMap>,
    req: WebRequest,
) -> WebResponse {
    let path = req.path.clone();
    let path = path[1..].split('/').collect::<Vec<_>>();

    if path.len() > 0 && path[0] == "static" {
        let path = cfg.mozsearch_path.clone() + &req.path;
        return handle_static(path, None);
    }

    if path.len() < 2 {
        return not_found();
    }

    let tree_name = &path[0];
    let kind = &path[1];

    println!("DBG {:?} {} {}", path, tree_name, kind);

    match &kind[..] {
        "rev" => {
            if path.len() < 3 {
                return not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_path(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => {
                    let output = String::from_utf8(writer).unwrap();
                    WebResponse {
                        status: StatusCode::Ok,
                        content_type: "text/html".to_owned(),
                        output: output,
                    }
                }
                Err(err) => WebResponse {
                    status: StatusCode::InternalServerError,
                    content_type: "text/plain".to_owned(),
                    output: err.to_owned(),
                },
            }
        }

        "source" => {
            let path = path.clone().split_off(2);
            let path = path.join("/");

            let tree_config = cfg.trees.get(*tree_name).unwrap();

            let path = format!("{}/file/{}", tree_config.paths.index_path, path);
            return handle_static(path, Some("text/html"));
        }

        "diff" => {
            if path.len() < 3 {
                return not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_diff(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => {
                    let output = String::from_utf8(writer).unwrap();
                    WebResponse {
                        status: StatusCode::Ok,
                        content_type: "text/html".to_owned(),
                        output: output,
                    }
                }
                Err(err) => WebResponse {
                    status: StatusCode::InternalServerError,
                    content_type: "text/plain".to_owned(),
                    output: err.to_owned(),
                },
            }
        }

        "commit" => {
            if path.len() < 3 {
                return not_found();
            }

            let rev = &path[2];

            let mut writer = Vec::new();
            match format::format_commit(cfg, &tree_name, &rev, &mut writer) {
                Ok(()) => {
                    let output = String::from_utf8(writer).unwrap();
                    WebResponse {
                        status: StatusCode::Ok,
                        content_type: "text/html".to_owned(),
                        output: output,
                    }
                }
                Err(err) => WebResponse {
                    status: StatusCode::InternalServerError,
                    content_type: "text/plain".to_owned(),
                    output: err.to_owned(),
                },
            }
        }

        "commit-info" => {
            if path.len() < 3 {
                return not_found();
            }

            let rev = &path[2];
            match blame::get_commit_info(&cfg, tree_name, rev) {
                Ok(json) => WebResponse {
                    status: StatusCode::Ok,
                    content_type: "application/json".to_owned(),
                    output: json,
                },
                Err(err) => WebResponse {
                    status: StatusCode::InternalServerError,
                    content_type: "text/plain".to_owned(),
                    output: err.to_owned(),
                },
            }
        }

        "complete" => {
            let ids = ident_map.get(&tree_name.to_string()).unwrap();
            let json = ids.lookup_json(&path[2], false, false, 6);
            WebResponse {
                status: StatusCode::Ok,
                content_type: "application/json".to_owned(),
                output: json,
            }
        }

        _ => not_found(),
    }
}

fn main() {
    env_logger::init();

    let cfg = config::load(&env::args().nth(1).unwrap(), true);

    // Dump config memory usage
    println!("{}", cfg.describe_mem_usage());

    let ident_map = IdentMap::load(&cfg);

    let internal_data = Mutex::new((cfg, ident_map));

    let handler = move |req: Request, mut res: Response| {
        if req.method != Method::Get {
            *res.status_mut() = StatusCode::MethodNotAllowed;
            let resp = format!("Invalid method").into_bytes();
            res.send(&resp).unwrap();
            return;
        }

        let path = match req.uri {
            uri::RequestUri::AbsolutePath(path) => path,
            uri::RequestUri::AbsoluteUri(url) => url.path().to_owned(),
            _ => panic!("Unexpected URI"),
        };

        let guard = match internal_data.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let (ref cfg, ref ident_map) = *guard;

        let response = handle(&cfg, &ident_map, WebRequest { path: path });

        *res.status_mut() = response.status;
        let output = response.output.into_bytes();
        let mime: Mime = response.content_type.parse().unwrap();
        res.headers_mut().set(ContentType(mime));
        res.send(&output).unwrap();
    };

    {
        // We *append* to the status file because other server components
        // also write to this file when they are done starting up, and we
        // don't want to clobber those messages.
        let mut status_out = OpenOptions::new()
            .append(true)
            .create(true)
            .open(&env::args().nth(2).unwrap())
            .unwrap();
        writeln!(status_out, "web-server.rs loaded").unwrap();
    }

    println!("On 8001");
    let _listening = hyper::Server::http("0.0.0.0:8001").unwrap().handle(handler);
}
<commit_msg>Bug 1669776 - Some refactoring around WebResponse struct creation<commit_after>extern crate env_logger;
extern crate hyper;
extern crate tools;

use std::collections::HashMap;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::BufReader;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Mutex;

use hyper::header::ContentType;
use hyper::method::Method;
use hyper::mime::Mime;
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri;

use tools::blame;
use tools::config;
use tools::file_format::identifiers::IdentMap;
use tools::format;

struct WebRequest {
    path: String,
}

struct WebResponse {
    status: StatusCode,
    content_type: String,
    output: String,
}

impl Default for WebResponse {
    fn default() -> WebResponse {
        WebResponse {
            status: StatusCode::Ok,
            content_type: "text/plain".to_owned(),
            output: String::new(),
        }
    }
}

impl WebResponse {
    fn html(body: String) -> WebResponse {
        WebResponse {
            content_type: "text/html".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn json(body: String) -> WebResponse {
        WebResponse {
            content_type: "application/json".to_owned(),
            output: body,
            .. WebResponse::default()
        }
    }

    fn internal_error(body: String) -> WebResponse {
        WebResponse {
            status: StatusCode::InternalServerError,
            output: body,
            .. WebResponse::default()
        }
    }

    fn not_found() -> WebResponse {
        WebResponse {
            status: StatusCode::NotFound,
            output: "Not found".to_owned(),
            .. WebResponse::default()
        }
    }
}

fn handle_static(path: String, content_type: Option<&str>) -> WebResponse {
    let source_file = match File::open(&path) {
        Ok(f) => f,
        Err(_) => {
            return WebResponse::not_found();
        }
    };
    let mut reader = BufReader::new(&source_file);
    let mut input = String::new();
    match reader.read_to_string(&mut input) {
        Ok(_) => {}
        Err(_) => {
            return WebResponse::not_found();
        }
    }

    let inferred_content_type = match Path::new(&path).extension() {
        Some(ext) => match ext.to_str().unwrap() {
            "css" => "text/css",
            "js" => "text/javascript",
            _ => "text/html",
        },
        None => "text/html",
    };
    let content_type = match content_type {
        Some(ct) => ct,
        None => inferred_content_type,
    };

    WebResponse {
        content_type: content_type.to_owned(),
        output: input,
        .. WebResponse::default()
    }
}

fn handle(
    cfg: &config::Config,
    ident_map: &HashMap<String, IdentMap>,
    req: WebRequest,
) -> WebResponse {
    let path = req.path.clone();
    let path = path[1..].split('/').collect::<Vec<_>>();

    if path.len() > 0 && path[0] == "static" {
        let path = cfg.mozsearch_path.clone() + &req.path;
        return handle_static(path, None);
    }

    if path.len() < 2 {
        return WebResponse::not_found();
    }

    let tree_name = &path[0];
    let kind = &path[1];

    println!("DBG {:?} {} {}", path, tree_name, kind);

    match &kind[..] {
        "rev" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_path(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "source" => {
            let path = path.clone().split_off(2);
            let path = path.join("/");

            let tree_config = &cfg.trees[*tree_name];

            let path = format!("{}/file/{}", tree_config.paths.index_path, path);
            return handle_static(path, Some("text/html"));
        }

        "diff" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            let path = path.clone().split_off(3);
            let path = path.join("/");

            let mut writer = Vec::new();
            match format::format_diff(cfg, &tree_name, &rev, &path, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];

            let mut writer = Vec::new();
            match format::format_commit(cfg, &tree_name, &rev, &mut writer) {
                Ok(()) => WebResponse::html(String::from_utf8(writer).unwrap()),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "commit-info" => {
            if path.len() < 3 {
                return WebResponse::not_found();
            }

            let rev = &path[2];
            match blame::get_commit_info(&cfg, tree_name, rev) {
                Ok(json) => WebResponse::json(json),
                Err(err) => WebResponse::internal_error(err.to_owned()),
            }
        }

        "complete" => {
            let ids = ident_map.get(&tree_name.to_string()).unwrap();
            let json = ids.lookup_json(&path[2], false, false, 6);
            WebResponse::json(json)
        }

        _ => WebResponse::not_found(),
    }
}

fn main() {
    env_logger::init();

    let cfg = config::load(&env::args().nth(1).unwrap(), true);

    // Dump config memory usage
    println!("{}", cfg.describe_mem_usage());

    let ident_map = IdentMap::load(&cfg);

    let internal_data = Mutex::new((cfg, ident_map));

    let handler = move |req: Request, mut res: Response| {
        if req.method != Method::Get {
            *res.status_mut() = StatusCode::MethodNotAllowed;
            let resp = format!("Invalid method").into_bytes();
            res.send(&resp).unwrap();
            return;
        }

        let path = match req.uri {
            uri::RequestUri::AbsolutePath(path) => path,
            uri::RequestUri::AbsoluteUri(url) => url.path().to_owned(),
            _ => panic!("Unexpected URI"),
        };

        let guard = match internal_data.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let (ref cfg, ref ident_map) = *guard;

        let response = handle(&cfg, &ident_map, WebRequest { path: path });

        *res.status_mut() = response.status;
        let output = response.output.into_bytes();
        let mime: Mime = response.content_type.parse().unwrap();
        res.headers_mut().set(ContentType(mime));
        res.send(&output).unwrap();
    };

    {
        // We *append* to the status file because other server components
        // also write to this file when they are done starting up, and we
        // don't want to clobber those messages.
        let mut status_out = OpenOptions::new()
            .append(true)
            .create(true)
            .open(&env::args().nth(2).unwrap())
            .unwrap();
        writeln!(status_out, "web-server.rs loaded").unwrap();
    }

    println!("On 8001");
    let _listening = hyper::Server::http("0.0.0.0:8001").unwrap().handle(handler);
}
<|endoftext|>


<commit_before>use crate::cpu::*;
use crate::masm::{CondCode, Label, MacroAssembler};

pub const VEXM_0F: u8 = 1;
pub const VEXM_0F38: u8 = 2;
pub const VEXM_0F3A: u8 = 3;

pub const VEXL_Z: u8 = 0;
pub const VEXL_128: u8 = 0;
pub const VEXL_256: u8 = 1;

pub const VEXP_NONE: u8 = 0;
pub const VEXP_66: u8 = 1;
pub const VEXP_F3: u8 = 2;
pub const VEXP_F2: u8 = 3;

pub fn emit_u64(buf: &mut MacroAssembler, val: u64) {
    buf.emit_u64(val)
}

pub fn emit_u32(buf: &mut MacroAssembler, val: u32) {
    buf.emit_u32(val)
}

pub fn emit_u8(buf: &mut MacroAssembler, val: u8) {
    buf.emit_u8(val)
}

pub fn emit_op(buf: &mut MacroAssembler, opcode: u8) {
    buf.emit_u8(opcode);
}

pub fn emit_vex3_rxbm(buf: &mut MacroAssembler, r: u8, x: u8, b: u8, m: u8) {
    assert!(r == 0 || r == 1);
    assert!(x == 0 || x == 1);
    assert!(b == 0 || b == 1);
    assert!(m > 1 && m < 4);

    buf.emit_u8(0b11000100);
    buf.emit_u8(!r << 7 | (!x << 6) & 0b1000000 | (!b << 5) & 0b100000 | m);
}

pub fn emit_vex3_wvlp(buf: &mut MacroAssembler, w: bool, v: u8, l: u8, p: u8) {
    assert!(v < 16);
    assert!(l == 0 || l == 1);
    assert!(p < 4);

    buf.emit_u8((w as u8) << 7 | (!v << 3) & 0b1111000 | l << 2 | p);
}

pub fn emit_vex2(buf: &mut MacroAssembler, r: u8, v: u8, l: u8, p: u8) {
    assert!(r == 0 || r == 1);
    assert!(v < 16);
    assert!(l == 0 || l == 1);
    assert!(p < 4);

    buf.emit_u8(0b11000101);
    buf.emit_u8(!r << 7 | (!v << 3) & 0b1111000 | l << 2 | p);
}

pub fn emit_rex(buf: &mut MacroAssembler, w: bool, r: u8, x: u8, b: u8) {
    assert!(r == 0 || r == 1);
    assert!(x == 0 || x == 1);
    assert!(b == 0 || b == 1);

    buf.emit_u8(0x40 | (w as u8) << 3 | r << 2 | x << 1 | b);
}

pub fn emit_modrm(buf: &mut MacroAssembler, mode: u8, reg: u8, rm: u8) {
    assert!(mode < 4);
    assert!(reg < 8);
    assert!(rm < 8);

    buf.emit_u8(mode << 6 | reg << 3 | rm);
}

pub fn emit_sib(buf: &mut MacroAssembler, scale: u8, index: u8, base: u8) {
    assert!(scale < 4);
    assert!(index < 8);
    assert!(base < 8);

    buf.emit_u8(scale << 6 | index << 3 | base);
}

pub fn fits_i8(imm: i32) -> bool {
    imm == (imm as i8) as i32
}

pub fn emit_jcc(buf: &mut MacroAssembler, cond: CondCode, lbl: Label) {
    let opcode = match cond {
        CondCode::Zero | CondCode::Equal => 0x84,
        CondCode::NonZero | CondCode::NotEqual => 0x85,
        CondCode::Greater => 0x8F,
        CondCode::GreaterEq => 0x8D,
        CondCode::Less => 0x8C,
        CondCode::LessEq => 0x8E,
        CondCode::UnsignedGreater => 0x87,   // above
        CondCode::UnsignedGreaterEq => 0x83, // above or equal
        CondCode::UnsignedLess => 0x82,      // below
        CondCode::UnsignedLessEq => 0x86,    // below or equal
    };

    emit_op(buf, 0x0f);
    emit_op(buf, opcode);
    buf.emit_label(lbl);
}

pub fn emit_jmp(buf: &mut MacroAssembler, lbl: Label) {
    emit_op(buf, 0xe9);
    buf.emit_label(lbl);
}

pub fn emit_shlx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_66);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

pub fn emit_shrx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_F2);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

pub fn emit_sarx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_F3);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::masm::{CondCode, MacroAssembler};

    macro_rules! assert_emit {
        (
            $($expr:expr),*;
            $name:ident
        ) => {{
            let mut buf = MacroAssembler::new();
            $name(&mut buf);
            let expected = vec![$($expr,)*];

            assert_eq!(expected, buf.data());
        }};

        (
            $($expr:expr),*;
            $name:ident
            (
                    $($param:expr),+
            )
        ) => {{
            let mut buf = MacroAssembler::new();
            $name(&mut buf, $($param,)*);
            let expected = vec![$($expr,)*];
            let data = buf.data();

            if expected != data {
                print!("exp: ");

                for (ind, val) in expected.iter().enumerate() {
                    if ind > 0 { print!(", "); }

                    print!("{:02x}", val);
                }

                print!("\ngot: ");

                for (ind, val) in data.iter().enumerate() {
                    if ind > 0 { print!(", "); }

                    print!("{:02x}", val);
                }

                println!("");

                panic!("emitted code wrong.");
            }
        }};
    }

    #[test]
    fn test_fits8() {
        assert!(fits_i8(1));
        assert!(fits_i8(0));
        assert!(fits_i8(-1));
        assert!(fits_i8(127));
        assert!(fits_i8(-128));

        assert!(!fits_i8(128));
        assert!(!fits_i8(-129));
    }

    #[test]
    fn test_emit_jcc_zero() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Zero, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x84, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_non_zero() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::NonZero, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x85, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_greater() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Greater, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8F, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_greater_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::GreaterEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8D, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_less() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Less, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8C, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_less_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::LessEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8E, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_greater() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedGreater, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x87, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_greater_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedGreaterEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x83, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_less() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedLess, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x82, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_less_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedLessEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x86, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jmp() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jmp(&mut buf, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0xe9, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_int_shlx() {
        assert_emit!(0xC4, 0xE2, 0xE1, 0xF7, 0xC2; emit_shlx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x29, 0xF7, 0xC1; emit_shlx(false, R8, R9, R10));
    }

    #[test]
    fn test_int_shrx() {
        assert_emit!(0xC4, 0xE2, 0xE3, 0xF7, 0xC2; emit_shrx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x2B, 0xF7, 0xC1; emit_shrx(false, R8, R9, R10));
    }

    #[test]
    fn test_int_sarx() {
        assert_emit!(0xC4, 0xE2, 0xE2, 0xF7, 0xC2; emit_sarx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x22, 0xF7, 0xCA; emit_sarx(false, R9, R10, R11));
    }
}
<commit_msg>asm: remove unused functions<commit_after>use crate::cpu::*;
use crate::masm::{CondCode, Label, MacroAssembler};

pub const VEXM_0F: u8 = 1;
pub const VEXM_0F38: u8 = 2;
pub const VEXM_0F3A: u8 = 3;

pub const VEXL_Z: u8 = 0;
pub const VEXL_128: u8 = 0;
pub const VEXL_256: u8 = 1;

pub const VEXP_NONE: u8 = 0;
pub const VEXP_66: u8 = 1;
pub const VEXP_F3: u8 = 2;
pub const VEXP_F2: u8 = 3;

fn emit_u8(buf: &mut MacroAssembler, val: u8) {
    buf.emit_u8(val)
}

fn emit_vex3_rxbm(buf: &mut MacroAssembler, r: u8, x: u8, b: u8, m: u8) {
    assert!(r == 0 || r == 1);
    assert!(x == 0 || x == 1);
    assert!(b == 0 || b == 1);
    assert!(m > 1 && m < 4);

    buf.emit_u8(0b11000100);
    buf.emit_u8(!r << 7 | (!x << 6) & 0b1000000 | (!b << 5) & 0b100000 | m);
}

fn emit_vex3_wvlp(buf: &mut MacroAssembler, w: bool, v: u8, l: u8, p: u8) {
    assert!(v < 16);
    assert!(l == 0 || l == 1);
    assert!(p < 4);

    buf.emit_u8((w as u8) << 7 | (!v << 3) & 0b1111000 | l << 2 | p);
}

fn emit_modrm(buf: &mut MacroAssembler, mode: u8, reg: u8, rm: u8) {
    assert!(mode < 4);
    assert!(reg < 8);
    assert!(rm < 8);

    buf.emit_u8(mode << 6 | reg << 3 | rm);
}

pub fn emit_jcc(buf: &mut MacroAssembler, cond: CondCode, lbl: Label) {
    let opcode = match cond {
        CondCode::Zero | CondCode::Equal => 0x84,
        CondCode::NonZero | CondCode::NotEqual => 0x85,
        CondCode::Greater => 0x8F,
        CondCode::GreaterEq => 0x8D,
        CondCode::Less => 0x8C,
        CondCode::LessEq => 0x8E,
        CondCode::UnsignedGreater => 0x87,   // above
        CondCode::UnsignedGreaterEq => 0x83, // above or equal
        CondCode::UnsignedLess => 0x82,      // below
        CondCode::UnsignedLessEq => 0x86,    // below or equal
    };

    emit_u8(buf, 0x0f);
    emit_u8(buf, opcode);
    buf.emit_label(lbl);
}

pub fn emit_jmp(buf: &mut MacroAssembler, lbl: Label) {
    emit_u8(buf, 0xe9);
    buf.emit_label(lbl);
}

pub fn emit_shlx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_66);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

pub fn emit_shrx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_F2);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

pub fn emit_sarx(buf: &mut MacroAssembler, x64: bool, dest: Reg, lhs: Reg, rhs: Reg) {
    emit_vex3_rxbm(buf, dest.msb(), 0, lhs.msb(), VEXM_0F38);
    emit_vex3_wvlp(buf, x64, rhs.int(), VEXL_Z, VEXP_F3);
    emit_u8(buf, 0xF7);
    emit_modrm(buf, 0b11, dest.and7(), lhs.and7());
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::masm::{CondCode, MacroAssembler};

    macro_rules! assert_emit {
        (
            $($expr:expr),*;
            $name:ident
        ) => {{
            let mut buf = MacroAssembler::new();
            $name(&mut buf);
            let expected = vec![$($expr,)*];

            assert_eq!(expected, buf.data());
        }};

        (
            $($expr:expr),*;
            $name:ident
            (
                    $($param:expr),+
            )
        ) => {{
            let mut buf = MacroAssembler::new();
            $name(&mut buf, $($param,)*);
            let expected = vec![$($expr,)*];
            let data = buf.data();

            if expected != data {
                print!("exp: ");

                for (ind, val) in expected.iter().enumerate() {
                    if ind > 0 { print!(", "); }

                    print!("{:02x}", val);
                }

                print!("\ngot: ");

                for (ind, val) in data.iter().enumerate() {
                    if ind > 0 { print!(", "); }

                    print!("{:02x}", val);
                }

                println!("");

                panic!("emitted code wrong.");
            }
        }};
    }

    #[test]
    fn test_emit_jcc_zero() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Zero, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x84, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_non_zero() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::NonZero, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x85, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_greater() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Greater, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8F, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_greater_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::GreaterEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8D, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_less() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::Less, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8C, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_less_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::LessEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x8E, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_greater() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedGreater, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x87, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_greater_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedGreaterEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x83, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_less() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedLess, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x82, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jcc_unsigned_less_or_equal() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jcc(&mut buf, CondCode::UnsignedLessEq, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0x0f, 0x86, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_emit_jmp() {
        let mut buf = MacroAssembler::new();
        let lbl = buf.create_label();
        emit_jmp(&mut buf, lbl);
        buf.nop();
        buf.bind_label(lbl);
        assert_eq!(vec![0xe9, 1, 0, 0, 0, 0x90], buf.data());
    }

    #[test]
    fn test_int_shlx() {
        assert_emit!(0xC4, 0xE2, 0xE1, 0xF7, 0xC2; emit_shlx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x29, 0xF7, 0xC1; emit_shlx(false, R8, R9, R10));
    }

    #[test]
    fn test_int_shrx() {
        assert_emit!(0xC4, 0xE2, 0xE3, 0xF7, 0xC2; emit_shrx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x2B, 0xF7, 0xC1; emit_shrx(false, R8, R9, R10));
    }

    #[test]
    fn test_int_sarx() {
        assert_emit!(0xC4, 0xE2, 0xE2, 0xF7, 0xC2; emit_sarx(true, RAX, RDX, RBX));
        assert_emit!(0xC4, 0x42, 0x22, 0xF7, 0xCA; emit_sarx(false, R9, R10, R11));
    }
}
<|endoftext|>


<commit_before>use cairo;

use librsvg::{rsvg_convert_only::LegacySize, CairoRenderer};

use crate::utils::load_svg;

#[test]
fn just_viewbox_uses_viewbox_size() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 200"/>
"#,
    )
        .unwrap();

    assert_eq!(
        CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
        (cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 100.0,
            height: 200.0,
        },
         cairo::Rectangle {
             x: 0.0,
             y: 0.0,
             width: 100.0,
             height: 200.0,
         })
    );
}
<commit_msg>Test for no intrinsic size at all<commit_after>use cairo;

use librsvg::{rsvg_convert_only::LegacySize, CairoRenderer};

use crate::utils::load_svg;

#[test]
fn just_viewbox_uses_viewbox_size() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 200"/>
"#,
    )
        .unwrap();

    assert_eq!(
        CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
        (cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 100.0,
            height: 200.0,
        },
         cairo::Rectangle {
             x: 0.0,
             y: 0.0,
             width: 100.0,
             height: 200.0,
         })
    );
}

#[test]
fn no_intrinsic_size_uses_element_geometries() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="20" width="30" height="40" fill="black"/>
</svg>
"#,
    )
        .unwrap();

    assert_eq!(
        CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
        (cairo::Rectangle {
            x: 10.0,
            y: 20.0,
            width: 30.0,
            height: 40.0,
        },
         cairo::Rectangle {
             x: 10.0,
             y: 20.0,
             width: 30.0,
             height: 40.0,
         })
    );
}
<|endoftext|>


<commit_before>#![recursion_limit = "128"]

#[macro_use]
extern crate quote;
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate if_chain;
extern crate validator;

use std::collections::HashMap;

use proc_macro::TokenStream;
use quote::ToTokens;

use validator::Validator;

mod lit;
mod validation;
mod asserts;
mod quoting;

use lit::*;
use validation::*;
use asserts::{assert_string_type, assert_type_matches, assert_has_len, assert_has_range};
use quoting::{FieldQuoter, quote_field_validation, quote_schema_validation};

#[proc_macro_derive(Validate, attributes(validate))]
pub fn derive_validation(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    // Parse the string representation to an AST
    let ast = syn::parse_macro_input(&source).unwrap();

    let expanded = impl_validate(&ast);
    expanded.parse().unwrap()
}

fn impl_validate(ast: &syn::MacroInput) -> quote::Tokens {
    // Ensure the macro is on a struct with named fields
    let fields = match ast.body {
        syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
            if fields.iter().any(|field| field.ident.is_none()) {
                panic!("struct has unnamed fields");
            }
            fields
        },
        _ => panic!("#[derive(Validate)] can only be used with structs"),
    };

    let mut validations = vec![];

    let field_types = find_fields_type(&fields);

    for field in fields {
        let field_ident = field.ident.clone().unwrap();
        let (name, field_validations) = find_validators_for_field(field, &field_types);
        let field_type = field_types.get(&field_ident.to_string()).cloned().unwrap();
        let field_quoter = FieldQuoter::new(field_ident, name, field_type);

        for validation in &field_validations {
            validations.push(quote_field_validation(&field_quoter, validation));
        }
    }

    let schema_validation = quote_schema_validation(find_struct_validation(&ast.attrs));

    let ident = &ast.ident;

    // Helper is provided for handling complex generic types correctly and effortlessly
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let impl_ast = quote!(
        impl #impl_generics Validate for #ident #ty_generics #where_clause {
            fn validate(&self) -> ::std::result::Result<(), ::validator::ValidationErrors> {
                let mut errors = ::validator::ValidationErrors::new();

                #(#validations)*

                #schema_validation

                if errors.is_empty() {
                    ::std::result::Result::Ok(())
                } else {
                    ::std::result::Result::Err(errors)
                }
            }
        }
    );
    // println!("{}", impl_ast.to_string());
    impl_ast
}

/// Find if a struct has some schema validation and returns the info if so
fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaValidation> {
    let error = |msg: &str| -> ! {
        panic!("Invalid schema level validation: {}", msg);
    };

    for attr in struct_attrs {
        if attr.value.name() != "validate" {
            continue;
        }

        if_chain! {
            if let syn::MetaItem::List(_, ref meta_items) = attr.value;
            if let syn::NestedMetaItem::MetaItem(ref item) = meta_items[0];
            if let &syn::MetaItem::List(ref ident2, ref args) = item;

            then {
                if ident2 != "schema" {
                    error("Only `schema` is allowed as validator on a struct")
                }

                let mut function = String::new();
                let mut skip_on_field_errors = true;
                let mut code = None;
                let mut message = None;

                for arg in args {
                    if_chain! {
                        if let syn::NestedMetaItem::MetaItem(ref item) = *arg;
                        if let syn::MetaItem::NameValue(ref name, ref val) = *item;

                        then {
                            match name.to_string().as_ref() {
                                "function" => {
                                    function = match lit_to_string(val) {
                                        Some(s) => s,
                                        None => error("invalid argument type for `function` \
                                        : only a string is allowed"),
                                    };
                                },
                                "skip_on_field_errors" => {
                                    skip_on_field_errors = match lit_to_bool(val) {
                                        Some(s) => s,
                                        None => error("invalid argument type for `skip_on_field_errors` \
                                        : only a bool is allowed"),
                                    };
                                },
                                "code" => {
                                    code = match lit_to_string(val) {
                                        Some(s) => Some(s),
                                        None => error("invalid argument type for `code` \
                                        : only a string is allowed"),
                                    };
                                },
                                "message" => {
                                    message = match lit_to_string(val) {
                                        Some(s) => Some(s),
                                        None => error("invalid argument type for `message` \
                                        : only a string is allowed"),
                                    };
                                },
                                _ => error("Unknown argument")
                            }
                        } else {
                            error("Unexpected args")
                        }
                    }
                }

                if function == "" {
                    error("`function` is required");
                }

                return Some(
                    SchemaValidation {
                        function,
                        skip_on_field_errors,
                        code,
                        message,
                    }
                );
            } else {
                error("Unexpected struct validator")
            }
        }
    }

    None
}

/// Find the types (as string) for each field of the struct
/// Needed for the `must_match` filter
fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> {
    let mut types = HashMap::new();

    for field in fields {
        let field_ident = field.ident.clone().unwrap().to_string();
        let field_type = match field.ty {
            syn::Ty::Path(_, ref p) => {
                let mut tokens = quote::Tokens::new();
                p.to_tokens(&mut tokens);
                tokens.to_string().replace(' ', "")

            },
            syn::Ty::Rptr(ref l, ref p) => {
                let mut tokens = quote::Tokens::new();
                p.ty.to_tokens(&mut tokens);
                let mut name = tokens.to_string().replace(' ', "");
                if l.is_some() {
                    name.insert(0, '&')
                }
                name
            },
            _ => panic!("Type `{:?}` of field `{}` not supported", field.ty, field_ident)
        };

        //println!("{:?}", field_type);
        types.insert(field_ident, field_type);
    }

    types
}

/// Find everything we need to know about a field: its real name if it's changed from the serialization
/// and the list of validators to run on it
fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, String>) -> (String, Vec<FieldValidation>) {
    let rust_ident = field.ident.clone().unwrap().to_string();
    let mut field_ident = field.ident.clone().unwrap().to_string();

    let error = |msg: &str| -> ! {
        panic!("Invalid attribute #[validate] on field `{}`: {}", field.ident.clone().unwrap().to_string(), msg);
    };

    let field_type = field_types.get(&field_ident).unwrap();

    let mut validators = vec![];
    let mut has_validate = false;

    for attr in &field.attrs {
        if attr.name() != "validate" && attr.name() != "serde" {
            continue;
        }

        if attr.name() == "validate" {
            has_validate = true;
        }

        match attr.value {
            syn::MetaItem::List(_, ref meta_items) => {
                // original name before serde rename
                if attr.name() == "serde" {
                    if let Some(s) = find_original_field_name(meta_items) {
                        field_ident = s;
                    }
                    continue;
                }

                // only validation from there on
                for meta_item in meta_items {
                    match *meta_item {
                        syn::NestedMetaItem::MetaItem(ref item) => match *item {
                            // email, url, phone
                            syn::MetaItem::Word(ref name) => match name.to_string().as_ref() {
                                "email" => {
                                    assert_string_type("email", field_type);
                                    validators.push(FieldValidation::new(Validator::Email));
                                },
                                "url" => {
                                    assert_string_type("url", field_type);
                                    validators.push(FieldValidation::new(Validator::Url));
                                },
                                #[cfg(feature = "phone")]
                                "phone" => {
                                    assert_string_type("phone", field_type);
                                    validators.push(FieldValidation::new(Validator::Phone));
                                },
                                "credit_card" => {
                                    assert_string_type("credit_card", field_type);
                                    validators.push(FieldValidation::new(Validator::CreditCard));
                                },
                                _ => panic!("Unexpected validator: {}", name)
                            },
                            // custom, contains, must_match, regex
                            syn::MetaItem::NameValue(ref name, ref val) => {
                                match name.to_string().as_ref() {
                                    "custom" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Custom(s))),
                                            None => error("invalid argument for `custom` validator: only strings are allowed"),
                                        };
                                    },
                                    "contains" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Contains(s))),
                                            None => error("invalid argument for `contains` validator: only strings are allowed"),
                                        };
                                    },
                                    "regex" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Regex(s))),
                                            None => error("invalid argument for `regex` validator: only strings are allowed"),
                                        };
                                    }
                                    "must_match" => {
                                        match lit_to_string(val) {
                                            Some(s) => {
                                                assert_type_matches(rust_ident.clone(), field_type, field_types.get(&s));
                                                validators.push(FieldValidation::new(Validator::MustMatch(s)));
                                            },
                                            None => error("invalid argument for `must_match` validator: only strings are allowed"),
                                        };
                                    },
                                    _ => panic!("unexpected name value validator: {:?}", name),
                                };
                            },
                            // Validators with several args
                            syn::MetaItem::List(ref name, ref meta_items) => match name.to_string().as_ref() {
                                "length" => {
                                    assert_has_len(rust_ident.clone(), field_type);
                                    validators.push(extract_length_validation(rust_ident.clone(), meta_items));
                                },
                                "range" => {
                                    assert_has_range(rust_ident.clone(), field_type);
                                    validators.push(extract_range_validation(rust_ident.clone(), meta_items));
                                },
                                "email" | "url" | "phone" | "credit_card" => {
                                    validators.push(extract_argless_validation(name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "custom" => {
                                    validators.push(extract_one_arg_validation("function", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "contains" => {
                                    validators.push(extract_one_arg_validation("pattern", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "regex" => {
                                    validators.push(extract_one_arg_validation("path", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "must_match" => {
                                    let validation = extract_one_arg_validation("other", name.to_string(), rust_ident.clone(), meta_items);
                                    if let Validator::MustMatch(ref t2) = validation.validator {
                                        assert_type_matches(rust_ident.clone(), field_type, field_types.get(t2));
                                    }
                                    validators.push(validation);
                                },
                                _ => panic!("unexpected list validator: {:?}", name.to_string())
                            },
                        },
                        _ => unreachable!("Found a non MetaItem while looking for validators")
                    };
                }
            },
            _ => unreachable!("Got something other than a list of attributes while checking field `{}`", field_ident),
        }
    }

    if has_validate && validators.is_empty() {
        error("it needs at least one validator");
    }

    (field_ident, validators)
}

/// Serde can be used to rename fields on deserialization but most of the times
/// we want the error on the original field.
///
/// For example a JS frontend might send camelCase fields and Rust converts them to snake_case
/// but we want to send the errors back with the original name
fn find_original_field_name(meta_items: &Vec<syn::NestedMetaItem>) -> Option<String> {
    let mut original_name = None;

    for meta_item in meta_items {
        match *meta_item {
            syn::NestedMetaItem::MetaItem(ref item) => match *item {
                syn::MetaItem::Word(_) => continue,
                syn::MetaItem::NameValue(ref name, ref val) => {
                    if name == "rename" {
                        original_name = Some(lit_to_string(val).unwrap());
                    }
                },
                syn::MetaItem::List(_, ref meta_items) => {
                    return find_original_field_name(meta_items);
                }
            },
            _ => unreachable!()
        };

        if original_name.is_some() {
            return original_name;
        }
    }

    original_name
}

<commit_msg>Don't emit a warning when a struct with no validator derives Validate<commit_after>#![recursion_limit = "128"]

#[macro_use]
extern crate quote;
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate if_chain;
extern crate validator;

use std::collections::HashMap;

use proc_macro::TokenStream;
use quote::ToTokens;

use validator::Validator;

mod lit;
mod validation;
mod asserts;
mod quoting;

use lit::*;
use validation::*;
use asserts::{assert_string_type, assert_type_matches, assert_has_len, assert_has_range};
use quoting::{FieldQuoter, quote_field_validation, quote_schema_validation};

#[proc_macro_derive(Validate, attributes(validate))]
pub fn derive_validation(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    // Parse the string representation to an AST
    let ast = syn::parse_macro_input(&source).unwrap();

    let expanded = impl_validate(&ast);
    expanded.parse().unwrap()
}

fn impl_validate(ast: &syn::MacroInput) -> quote::Tokens {
    // Ensure the macro is on a struct with named fields
    let fields = match ast.body {
        syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
            if fields.iter().any(|field| field.ident.is_none()) {
                panic!("struct has unnamed fields");
            }
            fields
        },
        _ => panic!("#[derive(Validate)] can only be used with structs"),
    };

    let mut validations = vec![];

    let field_types = find_fields_type(&fields);

    for field in fields {
        let field_ident = field.ident.clone().unwrap();
        let (name, field_validations) = find_validators_for_field(field, &field_types);
        let field_type = field_types.get(&field_ident.to_string()).cloned().unwrap();
        let field_quoter = FieldQuoter::new(field_ident, name, field_type);

        for validation in &field_validations {
            validations.push(quote_field_validation(&field_quoter, validation));
        }
    }

    let schema_validation = quote_schema_validation(find_struct_validation(&ast.attrs));

    let ident = &ast.ident;

    // Helper is provided for handling complex generic types correctly and effortlessly
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    let impl_ast = quote!(
        impl #impl_generics Validate for #ident #ty_generics #where_clause {
            #[allow(unused_mut)]
            fn validate(&self) -> ::std::result::Result<(), ::validator::ValidationErrors> {
                let mut errors = ::validator::ValidationErrors::new();

                #(#validations)*

                #schema_validation

                if errors.is_empty() {
                    ::std::result::Result::Ok(())
                } else {
                    ::std::result::Result::Err(errors)
                }
            }
        }
    );
    // println!("{}", impl_ast.to_string());
    impl_ast
}

/// Find if a struct has some schema validation and returns the info if so
fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaValidation> {
    let error = |msg: &str| -> ! {
        panic!("Invalid schema level validation: {}", msg);
    };

    for attr in struct_attrs {
        if attr.value.name() != "validate" {
            continue;
        }

        if_chain! {
            if let syn::MetaItem::List(_, ref meta_items) = attr.value;
            if let syn::NestedMetaItem::MetaItem(ref item) = meta_items[0];
            if let &syn::MetaItem::List(ref ident2, ref args) = item;

            then {
                if ident2 != "schema" {
                    error("Only `schema` is allowed as validator on a struct")
                }

                let mut function = String::new();
                let mut skip_on_field_errors = true;
                let mut code = None;
                let mut message = None;

                for arg in args {
                    if_chain! {
                        if let syn::NestedMetaItem::MetaItem(ref item) = *arg;
                        if let syn::MetaItem::NameValue(ref name, ref val) = *item;

                        then {
                            match name.to_string().as_ref() {
                                "function" => {
                                    function = match lit_to_string(val) {
                                        Some(s) => s,
                                        None => error("invalid argument type for `function` \
                                        : only a string is allowed"),
                                    };
                                },
                                "skip_on_field_errors" => {
                                    skip_on_field_errors = match lit_to_bool(val) {
                                        Some(s) => s,
                                        None => error("invalid argument type for `skip_on_field_errors` \
                                        : only a bool is allowed"),
                                    };
                                },
                                "code" => {
                                    code = match lit_to_string(val) {
                                        Some(s) => Some(s),
                                        None => error("invalid argument type for `code` \
                                        : only a string is allowed"),
                                    };
                                },
                                "message" => {
                                    message = match lit_to_string(val) {
                                        Some(s) => Some(s),
                                        None => error("invalid argument type for `message` \
                                        : only a string is allowed"),
                                    };
                                },
                                _ => error("Unknown argument")
                            }
                        } else {
                            error("Unexpected args")
                        }
                    }
                }

                if function == "" {
                    error("`function` is required");
                }

                return Some(
                    SchemaValidation {
                        function,
                        skip_on_field_errors,
                        code,
                        message,
                    }
                );
            } else {
                error("Unexpected struct validator")
            }
        }
    }

    None
}

/// Find the types (as string) for each field of the struct
/// Needed for the `must_match` filter
fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> {
    let mut types = HashMap::new();

    for field in fields {
        let field_ident = field.ident.clone().unwrap().to_string();
        let field_type = match field.ty {
            syn::Ty::Path(_, ref p) => {
                let mut tokens = quote::Tokens::new();
                p.to_tokens(&mut tokens);
                tokens.to_string().replace(' ', "")

            },
            syn::Ty::Rptr(ref l, ref p) => {
                let mut tokens = quote::Tokens::new();
                p.ty.to_tokens(&mut tokens);
                let mut name = tokens.to_string().replace(' ', "");
                if l.is_some() {
                    name.insert(0, '&')
                }
                name
            },
            _ => panic!("Type `{:?}` of field `{}` not supported", field.ty, field_ident)
        };

        //println!("{:?}", field_type);
        types.insert(field_ident, field_type);
    }

    types
}

/// Find everything we need to know about a field: its real name if it's changed from the serialization
/// and the list of validators to run on it
fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, String>) -> (String, Vec<FieldValidation>) {
    let rust_ident = field.ident.clone().unwrap().to_string();
    let mut field_ident = field.ident.clone().unwrap().to_string();

    let error = |msg: &str| -> ! {
        panic!("Invalid attribute #[validate] on field `{}`: {}", field.ident.clone().unwrap().to_string(), msg);
    };

    let field_type = field_types.get(&field_ident).unwrap();

    let mut validators = vec![];
    let mut has_validate = false;

    for attr in &field.attrs {
        if attr.name() != "validate" && attr.name() != "serde" {
            continue;
        }

        if attr.name() == "validate" {
            has_validate = true;
        }

        match attr.value {
            syn::MetaItem::List(_, ref meta_items) => {
                // original name before serde rename
                if attr.name() == "serde" {
                    if let Some(s) = find_original_field_name(meta_items) {
                        field_ident = s;
                    }
                    continue;
                }

                // only validation from there on
                for meta_item in meta_items {
                    match *meta_item {
                        syn::NestedMetaItem::MetaItem(ref item) => match *item {
                            // email, url, phone
                            syn::MetaItem::Word(ref name) => match name.to_string().as_ref() {
                                "email" => {
                                    assert_string_type("email", field_type);
                                    validators.push(FieldValidation::new(Validator::Email));
                                },
                                "url" => {
                                    assert_string_type("url", field_type);
                                    validators.push(FieldValidation::new(Validator::Url));
                                },
                                #[cfg(feature = "phone")]
                                "phone" => {
                                    assert_string_type("phone", field_type);
                                    validators.push(FieldValidation::new(Validator::Phone));
                                },
                                "credit_card" => {
                                    assert_string_type("credit_card", field_type);
                                    validators.push(FieldValidation::new(Validator::CreditCard));
                                },
                                _ => panic!("Unexpected validator: {}", name)
                            },
                            // custom, contains, must_match, regex
                            syn::MetaItem::NameValue(ref name, ref val) => {
                                match name.to_string().as_ref() {
                                    "custom" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Custom(s))),
                                            None => error("invalid argument for `custom` validator: only strings are allowed"),
                                        };
                                    },
                                    "contains" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Contains(s))),
                                            None => error("invalid argument for `contains` validator: only strings are allowed"),
                                        };
                                    },
                                    "regex" => {
                                        match lit_to_string(val) {
                                            Some(s) => validators.push(FieldValidation::new(Validator::Regex(s))),
                                            None => error("invalid argument for `regex` validator: only strings are allowed"),
                                        };
                                    }
                                    "must_match" => {
                                        match lit_to_string(val) {
                                            Some(s) => {
                                                assert_type_matches(rust_ident.clone(), field_type, field_types.get(&s));
                                                validators.push(FieldValidation::new(Validator::MustMatch(s)));
                                            },
                                            None => error("invalid argument for `must_match` validator: only strings are allowed"),
                                        };
                                    },
                                    _ => panic!("unexpected name value validator: {:?}", name),
                                };
                            },
                            // Validators with several args
                            syn::MetaItem::List(ref name, ref meta_items) => match name.to_string().as_ref() {
                                "length" => {
                                    assert_has_len(rust_ident.clone(), field_type);
                                    validators.push(extract_length_validation(rust_ident.clone(), meta_items));
                                },
                                "range" => {
                                    assert_has_range(rust_ident.clone(), field_type);
                                    validators.push(extract_range_validation(rust_ident.clone(), meta_items));
                                },
                                "email" | "url" | "phone" | "credit_card" => {
                                    validators.push(extract_argless_validation(name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "custom" => {
                                    validators.push(extract_one_arg_validation("function", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "contains" => {
                                    validators.push(extract_one_arg_validation("pattern", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "regex" => {
                                    validators.push(extract_one_arg_validation("path", name.to_string(), rust_ident.clone(), meta_items));
                                },
                                "must_match" => {
                                    let validation = extract_one_arg_validation("other", name.to_string(), rust_ident.clone(), meta_items);
                                    if let Validator::MustMatch(ref t2) = validation.validator {
                                        assert_type_matches(rust_ident.clone(), field_type, field_types.get(t2));
                                    }
                                    validators.push(validation);
                                },
                                _ => panic!("unexpected list validator: {:?}", name.to_string())
                            },
                        },
                        _ => unreachable!("Found a non MetaItem while looking for validators")
                    };
                }
            },
            _ => unreachable!("Got something other than a list of attributes while checking field `{}`", field_ident),
        }
    }

    if has_validate && validators.is_empty() {
        error("it needs at least one validator");
    }

    (field_ident, validators)
}

/// Serde can be used to rename fields on deserialization but most of the times
/// we want the error on the original field.
///
/// For example a JS frontend might send camelCase fields and Rust converts them to snake_case
/// but we want to send the errors back with the original name
fn find_original_field_name(meta_items: &Vec<syn::NestedMetaItem>) -> Option<String> {
    let mut original_name = None;

    for meta_item in meta_items {
        match *meta_item {
            syn::NestedMetaItem::MetaItem(ref item) => match *item {
                syn::MetaItem::Word(_) => continue,
                syn::MetaItem::NameValue(ref name, ref val) => {
                    if name == "rename" {
                        original_name = Some(lit_to_string(val).unwrap());
                    }
                },
                syn::MetaItem::List(_, ref meta_items) => {
                    return find_original_field_name(meta_items);
                }
            },
            _ => unreachable!()
        };

        if original_name.is_some() {
            return original_name;
        }
    }

    original_name
}

<|endoftext|>


<commit_before>use std::time::Duration;

use futures::{Future, IntoFuture, Poll, Async};
use futures::sync::oneshot::spawn;
use tokio::runtime::Runtime;

use super::credential::CredentialsError;
use super::client::{TimeoutFuture, SignAndDispatchError};
use super::request::{HttpResponse, HttpDispatchError};

lazy_static! {
    static ref FALLBACK_RUNTIME: Runtime = Runtime::new().unwrap();
}

/// Future that is returned from all rusoto service APIs.
///
/// ## Mocking
///
/// To mock service traits, you can use the `From` implementation to create `RusotoFuture`
/// instance.
///
/// ```rust,ignore
/// use rusoto_core::RusotoFuture;
/// use rusoto_s3::*;
///
/// pub struct S3Mock;
///
/// impl S3 for S3Mock {
///     fn abort_multipart_upload(
///         &self,
///         _input: AbortMultipartUploadRequest,
///     ) -> RusotoFuture<AbortMultipartUploadOutput, AbortMultipartUploadError> {
///         unimplemented!();
///     }
///
///     ...
///
///     fn put_object(&self, input: PutObjectRequest) -> RusotoFuture<PutObjectOutput, PutObjectError> {
///         if input.bucket == "foo" {
///             Ok(PutObjectOutput {
///                 ..Default::default()
///             }).into()
///         } else {
///             Err(PutObjectError::Validation("Invalid bucket".to_string())).into()
///         }
///     }
///
///     ...
/// }
/// ```
pub struct RusotoFuture<T, E> {
    state: Option<RusotoFutureState<T, E>>
}

pub fn new<T, E>(
        future: Box<TimeoutFuture<Item=HttpResponse, Error=SignAndDispatchError> + Send>,
        handler: fn(HttpResponse) -> Box<Future<Item=T, Error=E> + Send>
    ) -> RusotoFuture<T, E>
{
    RusotoFuture { state: Some(RusotoFutureState::SignAndDispatch { future, handler }) }
}

impl<T, E> RusotoFuture<T, E> {
    /// Set the timeout on the future to the provided duration.
    ///
    /// Unlike `set_timeout` this method can be easily chained:
    ///
    /// ```rust,ignore
    /// let future = s3.head_object(...)
    ///     .with_timeout(Duration::from_secs(10));
    /// ```
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.set_timeout(timeout);
        self
    }

    /// Set the timeout on the future to the provided duration.
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn set_timeout(&mut self, timeout: Duration) {
        if let Some(RusotoFutureState::SignAndDispatch { ref mut future, .. }) = self.state {
            future.set_timeout(timeout);
        }
    }

    /// Clear the timeout on the future.
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn clear_timeout(&mut self) {
        if let Some(RusotoFutureState::SignAndDispatch { ref mut future, .. }) = self.state {
            future.clear_timeout();
        }
    }

    /// Blocks the current thread until the future has resolved.
    ///
    /// This is meant to provide a simple way for non-async consumers
    /// to work with rusoto.
    pub fn sync(self) -> Result<T, E>
        where T: Send + 'static,
              E: From<CredentialsError> + From<HttpDispatchError> + Send + 'static
    {
        spawn(self, &FALLBACK_RUNTIME.executor()).wait()
    }
}

impl<T, E> Future for RusotoFuture<T, E>
    where E: From<CredentialsError> + From<HttpDispatchError>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        match self.state.take().unwrap() {
            RusotoFutureState::SignAndDispatch { mut future, handler } => {
                match future.poll() {
                    Err(SignAndDispatchError::Credentials(err)) => Err(err.into()),
                    Err(SignAndDispatchError::Dispatch(err)) => Err(err.into()),
                    Ok(Async::Ready(response)) => {
                        self.state = Some(RusotoFutureState::RunningResponseHandler(handler(response)));
                        self.poll()
                    },
                    Ok(Async::NotReady) => {
                        self.state = Some(RusotoFutureState::SignAndDispatch { future, handler });
                        Ok(Async::NotReady)
                    }
                }
            },
            RusotoFutureState::RunningResponseHandler(mut future) => {
                match future.poll()? {
                    Async::Ready(value) => Ok(Async::Ready(value)),
                    Async::NotReady => {
                        self.state = Some(RusotoFutureState::RunningResponseHandler(future));
                        Ok(Async::NotReady)
                    }
                }
            }
        }
    }
}

enum RusotoFutureState<T, E> {
    SignAndDispatch {
        future: Box<TimeoutFuture<Item=HttpResponse, Error=SignAndDispatchError> + Send>,
        handler: fn(HttpResponse) -> Box<Future<Item=T, Error=E> + Send>
    },
    RunningResponseHandler(Box<Future<Item=T, Error=E> + Send>)
}

impl<T: Send + 'static, E: Send + 'static> From<Result<T, E>> for RusotoFuture<T, E> {
    fn from(value: Result<T, E>) -> Self {
        let fut = value.into_future();
        RusotoFuture {
            state: Some(RusotoFutureState::RunningResponseHandler(Box::new(fut))),
        }
    }
}

#[test]
fn rusoto_future_is_send() {
    fn is_send<T: Send>() {}

    is_send::<RusotoFuture<(), ()>>();
}

#[test]
fn rusuto_future_from_ok() {
    use std::error::Error;
    let fut: RusotoFuture<i32, Box<Error + Send + Sync>> = RusotoFuture::from(Ok(42));
    assert_eq!(fut.sync().unwrap(), 42);
}

#[test]
fn rusuto_future_from_err() {
    use std::error::Error;
    let fut: RusotoFuture<i32, Box<Error + Send + Sync>> =
        RusotoFuture::from("ab".parse::<i32>().map_err(|e| e.into()));
    assert!(fut.sync().is_err());
}
<commit_msg>Add RusotoFuture::from_future<commit_after>use std::time::Duration;

use futures::{Future, IntoFuture, Poll, Async};
use futures::sync::oneshot::spawn;
use tokio::runtime::Runtime;

use super::credential::CredentialsError;
use super::client::{TimeoutFuture, SignAndDispatchError};
use super::request::{HttpResponse, HttpDispatchError};

lazy_static! {
    static ref FALLBACK_RUNTIME: Runtime = Runtime::new().unwrap();
}

/// Future that is returned from all rusoto service APIs.
///
/// ## Mocking
///
/// To mock service traits, you can use [`RusotoFuture::from_future`] to create
/// [`RusotoFuture`] instance. You can also use the [`From`] implementation on
/// the [`Result`] value.
///
/// ```rust,ignore
/// use rusoto_core::RusotoFuture;
/// use rusoto_s3::*;
///
/// pub struct S3Mock;
///
/// impl S3 for S3Mock {
///     fn abort_multipart_upload(
///         &self,
///         _input: AbortMultipartUploadRequest,
///     ) -> RusotoFuture<AbortMultipartUploadOutput, AbortMultipartUploadError> {
///         unimplemented!();
///     }
///
///     ...
///
///     fn put_object(&self, input: PutObjectRequest) -> RusotoFuture<PutObjectOutput, PutObjectError> {
///         if input.bucket == "foo" {
///             Ok(PutObjectOutput {
///                 ..Default::default()
///             }).into()
///         } else {
///             Err(PutObjectError::Validation("Invalid bucket".to_string())).into()
///         }
///     }
///
///     ...
/// }
/// ```
pub struct RusotoFuture<T, E> {
    state: Option<RusotoFutureState<T, E>>
}

pub fn new<T, E>(
        future: Box<TimeoutFuture<Item=HttpResponse, Error=SignAndDispatchError> + Send>,
        handler: fn(HttpResponse) -> Box<Future<Item=T, Error=E> + Send>
    ) -> RusotoFuture<T, E>
{
    RusotoFuture { state: Some(RusotoFutureState::SignAndDispatch { future, handler }) }
}

impl<T, E> RusotoFuture<T, E> {
    /// Set the timeout on the future to the provided duration.
    ///
    /// Unlike `set_timeout` this method can be easily chained:
    ///
    /// ```rust,ignore
    /// let future = s3.head_object(...)
    ///     .with_timeout(Duration::from_secs(10));
    /// ```
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.set_timeout(timeout);
        self
    }

    /// Set the timeout on the future to the provided duration.
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn set_timeout(&mut self, timeout: Duration) {
        if let Some(RusotoFutureState::SignAndDispatch { ref mut future, .. }) = self.state {
            future.set_timeout(timeout);
        }
    }

    /// Clear the timeout on the future.
    ///
    /// This is only guaranteed to take effect when called before the future
    /// is polled for the first time.
    pub fn clear_timeout(&mut self) {
        if let Some(RusotoFutureState::SignAndDispatch { ref mut future, .. }) = self.state {
            future.clear_timeout();
        }
    }

    /// Blocks the current thread until the future has resolved.
    ///
    /// This is meant to provide a simple way for non-async consumers
    /// to work with rusoto.
    pub fn sync(self) -> Result<T, E>
        where T: Send + 'static,
              E: From<CredentialsError> + From<HttpDispatchError> + Send + 'static
    {
        spawn(self, &FALLBACK_RUNTIME.executor()).wait()
    }

    /// Wraps the provided future, mainly to mock the service response.
    ///
    /// ## Caution
    ///
    /// This is not intended to be used outside of the test case.
    /// In production, [`Box`] is recommended.
    pub fn from_future<F>(fut: F) -> Self
        where F: IntoFuture<Item = T, Error = E>,
              F::Future: Send + 'static,
    {
        let fut = fut.into_future();
        RusotoFuture {
            state: Some(RusotoFutureState::RunningResponseHandler(Box::new(fut))),
        }
    }
}

impl<T, E> Future for RusotoFuture<T, E>
    where E: From<CredentialsError> + From<HttpDispatchError>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> Poll<T, E> {
        match self.state.take().unwrap() {
            RusotoFutureState::SignAndDispatch { mut future, handler } => {
                match future.poll() {
                    Err(SignAndDispatchError::Credentials(err)) => Err(err.into()),
                    Err(SignAndDispatchError::Dispatch(err)) => Err(err.into()),
                    Ok(Async::Ready(response)) => {
                        self.state = Some(RusotoFutureState::RunningResponseHandler(handler(response)));
                        self.poll()
                    },
                    Ok(Async::NotReady) => {
                        self.state = Some(RusotoFutureState::SignAndDispatch { future, handler });
                        Ok(Async::NotReady)
                    }
                }
            },
            RusotoFutureState::RunningResponseHandler(mut future) => {
                match future.poll()? {
                    Async::Ready(value) => Ok(Async::Ready(value)),
                    Async::NotReady => {
                        self.state = Some(RusotoFutureState::RunningResponseHandler(future));
                        Ok(Async::NotReady)
                    }
                }
            }
        }
    }
}

enum RusotoFutureState<T, E> {
    SignAndDispatch {
        future: Box<TimeoutFuture<Item=HttpResponse, Error=SignAndDispatchError> + Send>,
        handler: fn(HttpResponse) -> Box<Future<Item=T, Error=E> + Send>
    },
    RunningResponseHandler(Box<Future<Item=T, Error=E> + Send>)
}

impl<T: Send + 'static, E: Send + 'static> From<Result<T, E>> for RusotoFuture<T, E> {
    fn from(value: Result<T, E>) -> Self {
        RusotoFuture::from_future(value)
    }
}

#[test]
fn rusoto_future_is_send() {
    fn is_send<T: Send>() {}

    is_send::<RusotoFuture<(), ()>>();
}

#[test]
fn rusuto_future_from_ok() {
    use std::error::Error;
    let fut: RusotoFuture<i32, Box<Error + Send + Sync>> = RusotoFuture::from(Ok(42));
    assert_eq!(fut.sync().unwrap(), 42);
}

#[test]
fn rusuto_future_from_err() {
    use std::error::Error;
    let fut: RusotoFuture<i32, Box<Error + Send + Sync>> =
        RusotoFuture::from("ab".parse::<i32>().map_err(|e| e.into()));
    assert!(fut.sync().is_err());
}

#[test]
fn rusoto_future_from_delay() {
    use std::error::Error;
    use std::time::{Duration, Instant};
    use tokio_timer::Delay;
    let deadline = Instant::now() + Duration::from_millis(500);
    let delay = Delay::new(deadline);
    let fut: RusotoFuture<i32, Box<Error + Send + Sync>> =
        RusotoFuture::from_future(delay.map_err(From::from).map(|_| 42));
    assert_eq!(fut.sync().unwrap(), 42);
    assert!(deadline <= Instant::now());
}
<|endoftext|>