<commit_before>/* automatically generated by rust-bindgen */

#[link(name = "virglrenderer")]
extern "C" {}

pub const VIRGL_RENDERER_CALLBACKS_VERSION: u32 = 2;
pub const VIRGL_RENDERER_USE_EGL: u32 = 1;
pub const VIRGL_RENDERER_THREAD_SYNC: u32 = 2;
pub const VIRGL_RENDERER_USE_GLX: u32 = 4;
pub const VIRGL_RENDERER_USE_SURFACELESS: u32 = 8;
pub const VIRGL_RENDERER_USE_GLES: u32 = 16;
pub const VIRGL_RES_BIND_DEPTH_STENCIL: u32 = 1;
pub const VIRGL_RES_BIND_RENDER_TARGET: u32 = 2;
pub const VIRGL_RES_BIND_SAMPLER_VIEW: u32 = 8;
pub const VIRGL_RES_BIND_VERTEX_BUFFER: u32 = 16;
pub const VIRGL_RES_BIND_INDEX_BUFFER: u32 = 32;
pub const VIRGL_RES_BIND_CONSTANT_BUFFER: u32 = 64;
pub const VIRGL_RES_BIND_STREAM_OUTPUT: u32 = 2048;
pub const VIRGL_RES_BIND_CURSOR: u32 = 65536;
pub const VIRGL_RES_BIND_CUSTOM: u32 = 131072;
pub const VIRGL_RES_BIND_SCANOUT: u32 = 262144;
pub const VIRGL_RES_BIND_SHARED: u32 = 1048576;
pub type __int32_t = ::std::os::raw::c_int;
pub type __uint32_t = ::std::os::raw::c_uint;
pub type __uint64_t = ::std::os::raw::c_ulong;
pub type va_list = __builtin_va_list;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct virgl_box {
    _unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct iovec {
    _unused: [u8; 0],
}
pub type virgl_renderer_gl_context = *mut ::std::os::raw::c_void;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_gl_ctx_param {
    pub version: ::std::os::raw::c_int,
    pub shared: bool,
    pub major_ver: ::std::os::raw::c_int,
    pub minor_ver: ::std::os::raw::c_int,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_callbacks {
    pub version: ::std::os::raw::c_int,
    pub write_fence: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, fence: u32),
    >,
    pub create_gl_context: ::std::option::Option<
        unsafe extern "C" fn(
            cookie: *mut ::std::os::raw::c_void,
            scanout_idx: ::std::os::raw::c_int,
            param: *mut virgl_renderer_gl_ctx_param,
        ) -> virgl_renderer_gl_context,
    >,
    pub destroy_gl_context: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, ctx: virgl_renderer_gl_context),
    >,
    pub make_current: ::std::option::Option<
        unsafe extern "C" fn(
            cookie: *mut ::std::os::raw::c_void,
            scanout_idx: ::std::os::raw::c_int,
            ctx: virgl_renderer_gl_context,
        ) -> ::std::os::raw::c_int,
    >,
    pub get_drm_fd: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int,
    >,
}
extern "C" {
    pub fn virgl_renderer_init(
        cookie: *mut ::std::os::raw::c_void,
        flags: ::std::os::raw::c_int,
        cb: *mut virgl_renderer_callbacks,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_poll();
}
extern "C" {
    pub fn virgl_renderer_get_cursor_data(
        resource_id: u32,
        width: *mut u32,
        height: *mut u32,
    ) -> *mut ::std::os::raw::c_void;
}
extern "C" {
    pub fn virgl_renderer_get_rect(
        resource_id: ::std::os::raw::c_int,
        iov: *mut iovec,
        num_iovs: ::std::os::raw::c_uint,
        offset: u32,
        x: ::std::os::raw::c_int,
        y: ::std::os::raw::c_int,
        width: ::std::os::raw::c_int,
        height: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_get_fd_for_texture(
        tex_id: u32,
        fd: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_get_fd_for_texture2(
        tex_id: u32,
        fd: *mut ::std::os::raw::c_int,
        stride: *mut ::std::os::raw::c_int,
        offset: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
pub const VIRGL_RENDERER_STRUCTURE_TYPE_NONE: virgl_renderer_structure_type_v0 = 0;
pub const VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY: virgl_renderer_structure_type_v0 = 1;
pub type virgl_renderer_structure_type_v0 = u32;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_resource_create_args {
    pub handle: u32,
    pub target: u32,
    pub format: u32,
    pub bind: u32,
    pub width: u32,
    pub height: u32,
    pub depth: u32,
    pub array_size: u32,
    pub last_level: u32,
    pub nr_samples: u32,
    pub flags: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_hdr {
    pub stype: u32,
    pub stype_version: u32,
    pub size: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_export_query {
    pub hdr: virgl_renderer_hdr,
    pub in_resource_id: u32,
    pub out_num_fds: u32,
    pub in_export_fds: u32,
    pub out_fourcc: u32,
    pub pad: u32,
    pub out_fds: [i32; 4usize],
    pub out_strides: [u32; 4usize],
    pub out_offsets: [u32; 4usize],
    pub out_modifier: u64,
}
pub type virgl_debug_callback_type = ::std::option::Option<
    unsafe extern "C" fn(fmt: *const ::std::os::raw::c_char, ap: *mut __va_list_tag),
>;
extern "C" {
    pub fn virgl_renderer_resource_create(
        args: *mut virgl_renderer_resource_create_args,
        iov: *mut iovec,
        num_iovs: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_import_eglimage(
        args: *mut virgl_renderer_resource_create_args,
        image: *mut ::std::os::raw::c_void,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_unref(res_handle: u32);
}
extern "C" {
    pub fn virgl_renderer_resource_set_priv(res_handle: u32, priv_: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_resource_get_priv(res_handle: u32) -> *mut ::std::os::raw::c_void;
}
extern "C" {
    pub fn virgl_renderer_context_create(
        handle: u32,
        nlen: u32,
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_context_destroy(handle: u32);
}
extern "C" {
    pub fn virgl_renderer_submit_cmd(
        buffer: *mut ::std::os::raw::c_void,
        ctx_id: ::std::os::raw::c_int,
        ndw: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_transfer_read_iov(
        handle: u32,
        ctx_id: u32,
        level: u32,
        stride: u32,
        layer_stride: u32,
        box_: *mut virgl_box,
        offset: u64,
        iov: *mut iovec,
        iovec_cnt: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_transfer_write_iov(
        handle: u32,
        ctx_id: u32,
        level: ::std::os::raw::c_int,
        stride: u32,
        layer_stride: u32,
        box_: *mut virgl_box,
        offset: u64,
        iovec: *mut iovec,
        iovec_cnt: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
}
extern "C" {
    pub fn virgl_renderer_fill_caps(set: u32, version: u32, caps: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_resource_attach_iov(
        res_handle: ::std::os::raw::c_int,
        iov: *mut iovec,
        num_iovs: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_detach_iov(
        res_handle: ::std::os::raw::c_int,
        iov: *mut *mut iovec,
        num_iovs: *mut ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_create_fence(
        client_fence_id: ::std::os::raw::c_int,
        ctx_id: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_force_ctx_0();
}
extern "C" {
    pub fn virgl_renderer_ctx_attach_resource(
        ctx_id: ::std::os::raw::c_int,
        res_handle: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_ctx_detach_resource(
        ctx_id: ::std::os::raw::c_int,
        res_handle: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_set_debug_callback(cb: virgl_debug_callback_type) -> virgl_debug_callback_type;
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_resource_info {
    pub handle: u32,
    pub virgl_format: u32,
    pub width: u32,
    pub height: u32,
    pub depth: u32,
    pub flags: u32,
    pub tex_id: u32,
    pub stride: u32,
    pub drm_fourcc: ::std::os::raw::c_int,
}
extern "C" {
    pub fn virgl_renderer_resource_get_info(
        res_handle: ::std::os::raw::c_int,
        info: *mut virgl_renderer_resource_info,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_cleanup(cookie: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_reset();
}
extern "C" {
    pub fn virgl_renderer_get_poll_fd() -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_execute(
        execute_args: *mut ::std::os::raw::c_void,
        execute_size: u32,
    ) -> ::std::os::raw::c_int;
}
pub type __builtin_va_list = [__va_list_tag; 1usize];
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __va_list_tag {
    pub gp_offset: ::std::os::raw::c_uint,
    pub fp_offset: ::std::os::raw::c_uint,
    pub overflow_arg_area: *mut ::std::os::raw::c_void,
    pub reg_save_area: *mut ::std::os::raw::c_void,
}
impl Default for __va_list_tag {
    fn default() -> Self {
        unsafe { ::std::mem::zeroed() }
    }
}
<commit_msg>gpu_renderer: update bindings<commit_after>/* automatically generated by rust-bindgen */

#[link(name = "virglrenderer")]
extern "C" {}

pub const VIRGL_RENDERER_CALLBACKS_VERSION: u32 = 2;
pub const VIRGL_RENDERER_USE_EGL: u32 = 1;
pub const VIRGL_RENDERER_THREAD_SYNC: u32 = 2;
pub const VIRGL_RENDERER_USE_GLX: u32 = 4;
pub const VIRGL_RENDERER_USE_SURFACELESS: u32 = 8;
pub const VIRGL_RENDERER_USE_GLES: u32 = 16;
pub const VIRGL_RES_BIND_DEPTH_STENCIL: u32 = 1;
pub const VIRGL_RES_BIND_RENDER_TARGET: u32 = 2;
pub const VIRGL_RES_BIND_SAMPLER_VIEW: u32 = 8;
pub const VIRGL_RES_BIND_VERTEX_BUFFER: u32 = 16;
pub const VIRGL_RES_BIND_INDEX_BUFFER: u32 = 32;
pub const VIRGL_RES_BIND_CONSTANT_BUFFER: u32 = 64;
pub const VIRGL_RES_BIND_STREAM_OUTPUT: u32 = 2048;
pub const VIRGL_RES_BIND_CURSOR: u32 = 65536;
pub const VIRGL_RES_BIND_CUSTOM: u32 = 131072;
pub const VIRGL_RES_BIND_SCANOUT: u32 = 262144;
pub const VIRGL_RES_BIND_SHARED: u32 = 1048576;
pub type __int32_t = ::std::os::raw::c_int;
pub type __uint32_t = ::std::os::raw::c_uint;
pub type __uint64_t = ::std::os::raw::c_ulong;
pub type va_list = __builtin_va_list;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct virgl_box {
    _unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct iovec {
    _unused: [u8; 0],
}
pub type virgl_renderer_gl_context = *mut ::std::os::raw::c_void;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_gl_ctx_param {
    pub version: ::std::os::raw::c_int,
    pub shared: bool,
    pub major_ver: ::std::os::raw::c_int,
    pub minor_ver: ::std::os::raw::c_int,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_callbacks {
    pub version: ::std::os::raw::c_int,
    pub write_fence: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, fence: u32),
    >,
    pub create_gl_context: ::std::option::Option<
        unsafe extern "C" fn(
            cookie: *mut ::std::os::raw::c_void,
            scanout_idx: ::std::os::raw::c_int,
            param: *mut virgl_renderer_gl_ctx_param,
        ) -> virgl_renderer_gl_context,
    >,
    pub destroy_gl_context: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void, ctx: virgl_renderer_gl_context),
    >,
    pub make_current: ::std::option::Option<
        unsafe extern "C" fn(
            cookie: *mut ::std::os::raw::c_void,
            scanout_idx: ::std::os::raw::c_int,
            ctx: virgl_renderer_gl_context,
        ) -> ::std::os::raw::c_int,
    >,
    pub get_drm_fd: ::std::option::Option<
        unsafe extern "C" fn(cookie: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int,
    >,
}
extern "C" {
    pub fn virgl_renderer_init(
        cookie: *mut ::std::os::raw::c_void,
        flags: ::std::os::raw::c_int,
        cb: *mut virgl_renderer_callbacks,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_poll();
}
extern "C" {
    pub fn virgl_renderer_get_cursor_data(
        resource_id: u32,
        width: *mut u32,
        height: *mut u32,
    ) -> *mut ::std::os::raw::c_void;
}
extern "C" {
    pub fn virgl_renderer_get_rect(
        resource_id: ::std::os::raw::c_int,
        iov: *mut iovec,
        num_iovs: ::std::os::raw::c_uint,
        offset: u32,
        x: ::std::os::raw::c_int,
        y: ::std::os::raw::c_int,
        width: ::std::os::raw::c_int,
        height: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_get_fd_for_texture(
        tex_id: u32,
        fd: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_get_fd_for_texture2(
        tex_id: u32,
        fd: *mut ::std::os::raw::c_int,
        stride: *mut ::std::os::raw::c_int,
        offset: *mut ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
pub const VIRGL_RENDERER_STRUCTURE_TYPE_NONE: virgl_renderer_structure_type_v0 = 0;
pub const VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY: virgl_renderer_structure_type_v0 = 1;
pub const VIRGL_RENDERER_STRUCTURE_TYPE_SUPPORTED_STRUCTURES: virgl_renderer_structure_type_v0 = 2;
pub type virgl_renderer_structure_type_v0 = u32;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_resource_create_args {
    pub handle: u32,
    pub target: u32,
    pub format: u32,
    pub bind: u32,
    pub width: u32,
    pub height: u32,
    pub depth: u32,
    pub array_size: u32,
    pub last_level: u32,
    pub nr_samples: u32,
    pub flags: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_hdr {
    pub stype: u32,
    pub stype_version: u32,
    pub size: u32,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_export_query {
    pub hdr: virgl_renderer_hdr,
    pub in_resource_id: u32,
    pub out_num_fds: u32,
    pub in_export_fds: u32,
    pub out_fourcc: u32,
    pub pad: u32,
    pub out_fds: [i32; 4usize],
    pub out_strides: [u32; 4usize],
    pub out_offsets: [u32; 4usize],
    pub out_modifier: u64,
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_supported_structures {
    pub hdr: virgl_renderer_hdr,
    pub in_stype_version: u32,
    pub out_supported_structures_mask: u32,
}
pub type virgl_debug_callback_type = ::std::option::Option<
    unsafe extern "C" fn(fmt: *const ::std::os::raw::c_char, ap: *mut __va_list_tag),
>;
extern "C" {
    pub fn virgl_renderer_resource_create(
        args: *mut virgl_renderer_resource_create_args,
        iov: *mut iovec,
        num_iovs: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_import_eglimage(
        args: *mut virgl_renderer_resource_create_args,
        image: *mut ::std::os::raw::c_void,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_unref(res_handle: u32);
}
extern "C" {
    pub fn virgl_renderer_resource_set_priv(res_handle: u32, priv_: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_resource_get_priv(res_handle: u32) -> *mut ::std::os::raw::c_void;
}
extern "C" {
    pub fn virgl_renderer_context_create(
        handle: u32,
        nlen: u32,
        name: *const ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_context_destroy(handle: u32);
}
extern "C" {
    pub fn virgl_renderer_submit_cmd(
        buffer: *mut ::std::os::raw::c_void,
        ctx_id: ::std::os::raw::c_int,
        ndw: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_transfer_read_iov(
        handle: u32,
        ctx_id: u32,
        level: u32,
        stride: u32,
        layer_stride: u32,
        box_: *mut virgl_box,
        offset: u64,
        iov: *mut iovec,
        iovec_cnt: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_transfer_write_iov(
        handle: u32,
        ctx_id: u32,
        level: ::std::os::raw::c_int,
        stride: u32,
        layer_stride: u32,
        box_: *mut virgl_box,
        offset: u64,
        iovec: *mut iovec,
        iovec_cnt: ::std::os::raw::c_uint,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
}
extern "C" {
    pub fn virgl_renderer_fill_caps(set: u32, version: u32, caps: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_resource_attach_iov(
        res_handle: ::std::os::raw::c_int,
        iov: *mut iovec,
        num_iovs: ::std::os::raw::c_int,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_detach_iov(
        res_handle: ::std::os::raw::c_int,
        iov: *mut *mut iovec,
        num_iovs: *mut ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_create_fence(
        client_fence_id: ::std::os::raw::c_int,
        ctx_id: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_force_ctx_0();
}
extern "C" {
    pub fn virgl_renderer_ctx_attach_resource(
        ctx_id: ::std::os::raw::c_int,
        res_handle: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_renderer_ctx_detach_resource(
        ctx_id: ::std::os::raw::c_int,
        res_handle: ::std::os::raw::c_int,
    );
}
extern "C" {
    pub fn virgl_set_debug_callback(cb: virgl_debug_callback_type) -> virgl_debug_callback_type;
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct virgl_renderer_resource_info {
    pub handle: u32,
    pub virgl_format: u32,
    pub width: u32,
    pub height: u32,
    pub depth: u32,
    pub flags: u32,
    pub tex_id: u32,
    pub stride: u32,
    pub drm_fourcc: ::std::os::raw::c_int,
}
extern "C" {
    pub fn virgl_renderer_resource_get_info(
        res_handle: ::std::os::raw::c_int,
        info: *mut virgl_renderer_resource_info,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_cleanup(cookie: *mut ::std::os::raw::c_void);
}
extern "C" {
    pub fn virgl_renderer_reset();
}
extern "C" {
    pub fn virgl_renderer_get_poll_fd() -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_execute(
        execute_args: *mut ::std::os::raw::c_void,
        execute_size: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_allocation_metadata(
        request: *const ::std::os::raw::c_void,
        response: *mut ::std::os::raw::c_void,
        request_size: u32,
        response_size: u32,
    ) -> ::std::os::raw::c_int;
}
extern "C" {
    pub fn virgl_renderer_resource_create_v2(
        resource_id: u32,
        guest_memory_type: u32,
        guest_caching_type: u32,
        size: u64,
        iovec: *const iovec,
        num_iovs: u32,
        args: *const ::std::os::raw::c_void,
        args_size: u32,
    ) -> ::std::os::raw::c_int;
}
pub type __builtin_va_list = [__va_list_tag; 1usize];
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __va_list_tag {
    pub gp_offset: ::std::os::raw::c_uint,
    pub fp_offset: ::std::os::raw::c_uint,
    pub overflow_arg_area: *mut ::std::os::raw::c_void,
    pub reg_save_area: *mut ::std::os::raw::c_void,
}
impl Default for __va_list_tag {
    fn default() -> Self {
        unsafe { ::std::mem::zeroed() }
    }
}
<|endoftext|>


<commit_before>#![macro_use]

use std::iter::Peekable;
use std::time::duration::Duration;
use time::Timespec;
use rustc_serialize::{Encoder, Encodable};

use error::MpdResult;
use client::MpdPair;

// Work around new orphan check: as I can't implement a foreign trait for foreign struct,
// I create local trait I can do anything I want with, blanket-implement foreign trait
// for it, and implement it for all foreign structs I need.
pub trait ForceEncodable {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error>;
}

impl<'a> Encodable for ForceEncodable + 'a {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        self.encode(s)
    }
}

impl ForceEncodable for Duration {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_i64(self.num_milliseconds())
    }
}
impl ForceEncodable for Timespec {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_i64(self.sec)
    }
}
impl<T: ForceEncodable> ForceEncodable for Option<T> {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_option(|s| match *self {
            Some(ref v) => s.emit_option_some(|s| v.encode(s)),
            None => s.emit_option_none()
        })
    }
}

pub struct FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    iter: &'a mut Peekable<MpdResult<MpdPair>, I>,
    field: &'a str,
    finished: bool
}

impl<'a, I> FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    pub fn new(iter: &'a mut Peekable<MpdResult<MpdPair>, I>, field: &'a str) -> FieldCutIter<'a, I> {
        FieldCutIter {
            iter: iter,
            field: field,
            finished: false
        }
    }
}

impl<'a, I> Iterator for FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    type Item = MpdResult<MpdPair>;
    fn next(&mut self) -> Option<MpdResult<MpdPair>> {
        if self.finished {
            return None;
        }

        let item = self.iter.next();
        self.finished = match self.iter.peek() {
            Some(&Ok(MpdPair(ref name, _))) if *name == self.field => true,
            None => true,
            _ => false
        };
        item
    }
}

impl<T1: ForceEncodable, T2: ForceEncodable> ForceEncodable for (T1, T2) {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_tuple(2, |s| {
            s.emit_tuple_arg(0, |s| self.0.encode(s)).and_then(|_|
            s.emit_tuple_arg(1, |s| self.1.encode(s)))
        })
    }
}

macro_rules! mpd_collectable {
    ($typ:ty, $first_field:expr) => {
        impl FromIterator<MpdResult<MpdPair>> for MpdResult<Vec<$typ>> {
            fn from_iter<I: Iterator<Item=MpdResult<MpdPair>>>(iterator: I) -> MpdResult<Vec<$typ>> {
                let mut iter = iterator.fuse().peekable();
                let mut result = Vec::new();

                while !iter.is_empty() {
                    result.push(try!(FieldCutIter::new(&mut iter, $first_field).collect()));
                }

                Ok(result)
            }
        }
    }
}
<commit_msg>peekable requires iterator only<commit_after>#![macro_use]

use std::iter::Peekable;
use std::time::duration::Duration;
use time::Timespec;
use rustc_serialize::{Encoder, Encodable};

use error::MpdResult;
use client::MpdPair;

// Work around new orphan check: as I can't implement a foreign trait for foreign struct,
// I create local trait I can do anything I want with, blanket-implement foreign trait
// for it, and implement it for all foreign structs I need.
pub trait ForceEncodable {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error>;
}

impl<'a> Encodable for ForceEncodable + 'a {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        self.encode(s)
    }
}

impl ForceEncodable for Duration {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_i64(self.num_milliseconds())
    }
}
impl ForceEncodable for Timespec {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_i64(self.sec)
    }
}
impl<T: ForceEncodable> ForceEncodable for Option<T> {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_option(|s| match *self {
            Some(ref v) => s.emit_option_some(|s| v.encode(s)),
            None => s.emit_option_none()
        })
    }
}

pub struct FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    iter: &'a mut Peekable<I>,
    field: &'a str,
    finished: bool
}

impl<'a, I> FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    pub fn new(iter: &'a mut Peekable<I>, field: &'a str) -> FieldCutIter<'a, I> {
        FieldCutIter {
            iter: iter,
            field: field,
            finished: false
        }
    }
}

impl<'a, I> Iterator for FieldCutIter<'a, I> where I: 'a + Iterator<Item=MpdResult<MpdPair>> {
    type Item = MpdResult<MpdPair>;
    fn next(&mut self) -> Option<MpdResult<MpdPair>> {
        if self.finished {
            return None;
        }

        let item = self.iter.next();
        self.finished = match self.iter.peek() {
            Some(&Ok(MpdPair(ref name, _))) if *name == self.field => true,
            None => true,
            _ => false
        };
        item
    }
}

impl<T1: ForceEncodable, T2: ForceEncodable> ForceEncodable for (T1, T2) {
    #[inline] fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_tuple(2, |s| {
            s.emit_tuple_arg(0, |s| self.0.encode(s)).and_then(|_|
            s.emit_tuple_arg(1, |s| self.1.encode(s)))
        })
    }
}

macro_rules! mpd_collectable {
    ($typ:ty, $first_field:expr) => {
        impl FromIterator<MpdResult<MpdPair>> for MpdResult<Vec<$typ>> {
            fn from_iter<I: Iterator<Item=MpdResult<MpdPair>>>(iterator: I) -> MpdResult<Vec<$typ>> {
                let mut iter = iterator.fuse().peekable();
                let mut result = Vec::new();

                while !iter.is_empty() {
                    result.push(try!(FieldCutIter::new(&mut iter, $first_field).collect()));
                }

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


<commit_before>use iron::prelude::*;
use maud::{Markup, PreEscaped, Render};
use pulldown_cmark;

use models::User;

static STYLES: &'static str = r#"
html {
    box-sizing: border-box;
    font: 100%/1.5 "Liberation Serif", "Times New Roman", serif;
    background: linear-gradient(to bottom, #445 0%, #001 100%);
}

@media (min-width: 450px) {
    html {
        font-size: 125%;
    }
}

@media (min-width: 750px) {
    html {
        font-size: 150%;
    }
}

input, button {
    font-size: 1rem;
    line-height: 1.5;
}

* {
    box-sizing: inherit;
}

body {
    margin: 0 auto;
    max-width: 30rem;
    min-height: 100vh;
    padding: 0.5rem 1.5rem;
    box-shadow: 0 0 0 8px #ccc, 0 0 0 16px #999, 0 0 0 20px #666;
    background: #fff;
}

a {
    text-decoration: none;
    color: #910;
}

a:hover, a:focus, a:active {
    text-shadow: 0 0 2px #fff, 0 0 8px #d60;
}

h1 {
    /* A E S T H E T I C */
    margin: 0 0 1rem;
    white-space: nowrap;
    font-family: "Comic Sans MS", sans-serif;
    font-size: 1.75rem;
    letter-spacing: 0.5rem;
}

@media (min-width: 600px) {
    h1 {
        font-size: 3rem;
        letter-spacing: 0.75rem;
    }
}

h1 a {
    color: #f90;
    text-transform: uppercase;
    text-shadow: 2px 2px #000, -2px -2px #faa;
    transition: text-shadow 0.2s;
}

h1 a:hover, h1 a:focus, h1 a:active {
    text-shadow: 16px 16px #000, -16px -16px #faa;
    transition: text-shadow 0.2s;
}

.karkinos {
    font-family: "Comic Sans MS", sans-serif;
}

#q {
    width: 100%;
    padding: 0.25rem 0.5rem;
    border: 1px solid #000;
    box-shadow: 0 0 0 0 #f90, 0 0 0 0 #666;
    transition: box-shadow 0.2s;
}

#q:focus {
    border-color: #f90;
    box-shadow: -2px -2px 0 2px #f90, 2px 2px 0 3px #666;
    transition: box-shadow 0.2s;
}

table {
    border-spacing: 0.5rem;
}

th {
    text-align: right;
    vertical-align: top;
}
"#;

pub fn layout(r: &Request, title: Option<&str>, body: Markup) -> Markup {
    let url = r.url.clone().into_generic_url();
    html! {
        (PreEscaped("<!DOCTYPE html>"))
        html {
            meta charset="utf-8" /
            title {
                @if let Some(title) = title {
                    (title) " - "
                }
                "Karkinos"
            }
            meta name="viewport" content="width=device-width" /
            style (PreEscaped(STYLES))
            body {
                h1 a href="/" "🦀Karkinos"
                @if let Some(title) = title {
                    h2 a href=(url.path()) title="Link to this page" (title)
                }
                (body)
            }
        }
    }
}

pub fn home(r: &Request) -> Markup {
    layout(r, None, html! {
        form action=(url_for!(r, "search")) {
            input name="q" id="q" type="search" placeholder="Enter a username" /
        }
        script (PreEscaped("document.getElementById('q').select()"))
        p {
            span.karkinos "KARKINOS"
            " is a database of people interested in the "
            a href="https://www.rust-lang.org" "Rust programming language"
            ". It uses the same data as "
            a href="http://rustaceans.org" "rustaceans.org"
            ", but presents it through a different interface."
        }
        p "I created Karkinos for these reasons:"
        ul {
            li "To provide access for users who browse with JavaScript disabled;"
            li "To rewrite the backend in Rust (instead of Node.js);"
            li {
                "As a proving ground for my template engine, "
                a href="https://github.com/lfairy/maud" "Maud"
                ";"
            }
            li "To screw around with CSS (this is the most important reason)."
        }
        p {
            "Karkinos is named after a very special "
            a href="https://en.wikipedia.org/wiki/Cancer_(constellation)#Names" "giant crab"
            "."
        }
        p {
            "The source code for this site can be found on "
            a href="https://github.com/lfairy/karkinos" "GitHub"
            "."
        }
    })
}

pub fn not_found(r: &Request) -> Markup {
    layout(r, Some("Not found"), html! {
        p {
            "The page at "
            strong (r.url)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

pub fn user(r: &Request, id: &str, user: &User) -> Markup {
    let title = if let Some(ref name) = user.name {
        format!("{} ({})", name, id)
    } else {
        id.to_string()
    };
    layout(r, Some(&title), html! {
        table {
            @if let Some(ref nick) = user.irc {
                tr {
                    th "IRC"
                    td {
                        (nick)
                        @if !user.irc_channels.is_empty() {
                            " on "
                            @for (i, channel) in user.irc_channels.iter().enumerate() {
                                @if i > 0 { ", " }
                                a href={ "irc://irc.mozilla.org/" (channel) } {
                                    "#" (channel)
                                }
                            }
                        }
                    }
                }
            }
            @if let Some(ref x) = user.discourse {
                tr {
                    th "Discourse"
                    td a href={ "https://users.rust-lang.org/users/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.reddit {
                tr {
                    th "Reddit"
                    td a href={ "https://reddit.com/user/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.twitter {
                tr {
                    th "Twitter"
                    td a href={ "https://twitter.com/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.website {
                tr {
                    th "Website"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.blog {
                tr {
                    th "Blog"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.email {
                tr {
                    th "Email"
                    td a href={ "mailto:" (x) } (x)
                }
            }
        }
        @if let Some(ref x) = user.notes {
            div.notes (Markdown(x))
        }
    })
}

pub fn user_error(r: &Request, id: &str, error: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " exists, but their entry could not be parsed."
        }
        p {
            "(Error: " (error) ")"
        }
    })
}

pub fn user_not_found(r: &Request, id: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

struct Markdown<'a>(&'a str);

impl<'a> Render for Markdown<'a> {
    fn render_to(&self, buffer: &mut String) {
        let parser = pulldown_cmark::Parser::new(self.0);
        pulldown_cmark::html::push_html(buffer, parser);
    }
}
<commit_msg>Use the full URL in permalink<commit_after>use iron::prelude::*;
use maud::{Markup, PreEscaped, Render};
use pulldown_cmark;

use models::User;

static STYLES: &'static str = r#"
html {
    box-sizing: border-box;
    font: 100%/1.5 "Liberation Serif", "Times New Roman", serif;
    background: linear-gradient(to bottom, #445 0%, #001 100%);
}

@media (min-width: 450px) {
    html {
        font-size: 125%;
    }
}

@media (min-width: 750px) {
    html {
        font-size: 150%;
    }
}

input, button {
    font-size: 1rem;
    line-height: 1.5;
}

* {
    box-sizing: inherit;
}

body {
    margin: 0 auto;
    max-width: 30rem;
    min-height: 100vh;
    padding: 0.5rem 1.5rem;
    box-shadow: 0 0 0 8px #ccc, 0 0 0 16px #999, 0 0 0 20px #666;
    background: #fff;
}

a {
    text-decoration: none;
    color: #910;
}

a:hover, a:focus, a:active {
    text-shadow: 0 0 2px #fff, 0 0 8px #d60;
}

h1 {
    /* A E S T H E T I C */
    margin: 0 0 1rem;
    white-space: nowrap;
    font-family: "Comic Sans MS", sans-serif;
    font-size: 1.75rem;
    letter-spacing: 0.5rem;
}

@media (min-width: 600px) {
    h1 {
        font-size: 3rem;
        letter-spacing: 0.75rem;
    }
}

h1 a {
    color: #f90;
    text-transform: uppercase;
    text-shadow: 2px 2px #000, -2px -2px #faa;
    transition: text-shadow 0.2s;
}

h1 a:hover, h1 a:focus, h1 a:active {
    text-shadow: 16px 16px #000, -16px -16px #faa;
    transition: text-shadow 0.2s;
}

.karkinos {
    font-family: "Comic Sans MS", sans-serif;
}

#q {
    width: 100%;
    padding: 0.25rem 0.5rem;
    border: 1px solid #000;
    box-shadow: 0 0 0 0 #f90, 0 0 0 0 #666;
    transition: box-shadow 0.2s;
}

#q:focus {
    border-color: #f90;
    box-shadow: -2px -2px 0 2px #f90, 2px 2px 0 3px #666;
    transition: box-shadow 0.2s;
}

table {
    border-spacing: 0.5rem;
}

th {
    text-align: right;
    vertical-align: top;
}
"#;

pub fn layout(r: &Request, title: Option<&str>, body: Markup) -> Markup {
    html! {
        (PreEscaped("<!DOCTYPE html>"))
        html {
            meta charset="utf-8" /
            title {
                @if let Some(title) = title {
                    (title) " - "
                }
                "Karkinos"
            }
            meta name="viewport" content="width=device-width" /
            style (PreEscaped(STYLES))
            body {
                h1 a href="/" "🦀Karkinos"
                @if let Some(title) = title {
                    h2 a href=(r.url) title="Link to this page" (title)
                }
                (body)
            }
        }
    }
}

pub fn home(r: &Request) -> Markup {
    layout(r, None, html! {
        form action=(url_for!(r, "search")) {
            input name="q" id="q" type="search" placeholder="Enter a username" /
        }
        script (PreEscaped("document.getElementById('q').select()"))
        p {
            span.karkinos "KARKINOS"
            " is a database of people interested in the "
            a href="https://www.rust-lang.org" "Rust programming language"
            ". It uses the same data as "
            a href="http://rustaceans.org" "rustaceans.org"
            ", but presents it through a different interface."
        }
        p "I created Karkinos for these reasons:"
        ul {
            li "To provide access for users who browse with JavaScript disabled;"
            li "To rewrite the backend in Rust (instead of Node.js);"
            li {
                "As a proving ground for my template engine, "
                a href="https://github.com/lfairy/maud" "Maud"
                ";"
            }
            li "To screw around with CSS (this is the most important reason)."
        }
        p {
            "Karkinos is named after a very special "
            a href="https://en.wikipedia.org/wiki/Cancer_(constellation)#Names" "giant crab"
            "."
        }
        p {
            "The source code for this site can be found on "
            a href="https://github.com/lfairy/karkinos" "GitHub"
            "."
        }
    })
}

pub fn not_found(r: &Request) -> Markup {
    layout(r, Some("Not found"), html! {
        p {
            "The page at "
            strong (r.url)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

pub fn user(r: &Request, id: &str, user: &User) -> Markup {
    let title = if let Some(ref name) = user.name {
        format!("{} ({})", name, id)
    } else {
        id.to_string()
    };
    layout(r, Some(&title), html! {
        table {
            @if let Some(ref nick) = user.irc {
                tr {
                    th "IRC"
                    td {
                        (nick)
                        @if !user.irc_channels.is_empty() {
                            " on "
                            @for (i, channel) in user.irc_channels.iter().enumerate() {
                                @if i > 0 { ", " }
                                a href={ "irc://irc.mozilla.org/" (channel) } {
                                    "#" (channel)
                                }
                            }
                        }
                    }
                }
            }
            @if let Some(ref x) = user.discourse {
                tr {
                    th "Discourse"
                    td a href={ "https://users.rust-lang.org/users/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.reddit {
                tr {
                    th "Reddit"
                    td a href={ "https://reddit.com/user/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.twitter {
                tr {
                    th "Twitter"
                    td a href={ "https://twitter.com/" (x) } (x)
                }
            }
            @if let Some(ref x) = user.website {
                tr {
                    th "Website"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.blog {
                tr {
                    th "Blog"
                    td a href=(x) (x)
                }
            }
            @if let Some(ref x) = user.email {
                tr {
                    th "Email"
                    td a href={ "mailto:" (x) } (x)
                }
            }
        }
        @if let Some(ref x) = user.notes {
            div.notes (Markdown(x))
        }
    })
}

pub fn user_error(r: &Request, id: &str, error: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " exists, but their entry could not be parsed."
        }
        p {
            "(Error: " (error) ")"
        }
    })
}

pub fn user_not_found(r: &Request, id: &str) -> Markup {
    layout(r, Some(id), html! {
        p {
            "The user "
            strong (id)
            " could not be found."
        }
        p a href="/" "‹ Back to home page"
    })
}

struct Markdown<'a>(&'a str);

impl<'a> Render for Markdown<'a> {
    fn render_to(&self, buffer: &mut String) {
        let parser = pulldown_cmark::Parser::new(self.0);
        pulldown_cmark::html::push_html(buffer, parser);
    }
}
<|endoftext|>


<commit_before>use super::behaviors::MouseBehavior;
use crate::prelude::*;
use crate::shell::{Key, KeyEvent};
use core::f64::MAX;
use rust_decimal::prelude::*;

pub static ID_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_BTN: &'static str = "numeric_box_button";

pub enum InputAction {
    Inc,
    Dec,
    ChangeByKey(KeyEvent),
    ChangeByMouseScroll(Point),
    Focus,
}

#[derive(Default, AsAny)]
pub struct NumericBoxState {
    pub action: Option<InputAction>,
    pub input: Entity,
    pub min: Decimal,
    pub max: Decimal,
    pub step: Decimal,
    pub current_value: Decimal,
}

impl NumericBoxState {
    fn action(&mut self, action: InputAction) {
        self.action = Some(action);
    }

    fn change_val(&mut self, new_value: Decimal, ctx: &mut Context) {
        self.current_value = self.max(self.min(new_value));
        ctx.get_widget(self.input)
            .set::<String16>("text", String16::from(self.current_value.to_string()));

        ctx.push_event_strategy_by_entity(
            ChangedEvent(ctx.entity),
            ctx.entity,
            EventStrategy::Direct,
        );
    }

    fn min(&self, d: Decimal) -> Decimal {
        if d < self.min {
            return self.min;
        } else {
            return d;
        }
    }

    fn max(&self, d: Decimal) -> Decimal {
       if d > self.max {
           return self.max;
       } else {
           return d;
       }
    }

    fn request_focus(&self, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("focused") {
            ctx.widget().set::<bool>("focused", true);
            ctx.push_event_by_window(FocusEvent::RequestFocus(ctx.entity));
        }
    }
}

fn default_or(key: &str, default_value: f64, ctx: &mut Context) -> Decimal {
    let property = ctx.widget().clone_or_default(key);

    match Decimal::from_f64(property) {
        Some(val) => val,
        None => Decimal::from_f64(default_value).unwrap(),
    }
}

impl State for NumericBoxState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.input = ctx.entity_of_child(ID_INPUT).expect(
            "NumericBoxState
        .init(): the child input could not be found!",
        );
        self.min = default_or("min", 0.0, ctx);
        self.max = default_or("max", MAX, ctx);
        self.step = default_or("step", 1.0, ctx);
        self.current_value = default_or("val", 0.0, ctx);

        let init_value = String16::from(self.current_value.to_string());
        ctx.get_widget(self.input)
            .set::<String16>("text", init_value);
    }

    // TODO: let the user type the value, or select it for cut, copy, paste operations
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if let Some(action) = &self.action {
            match action {
                InputAction::Inc => {
                    self.change_val(self.current_value + self.step, ctx);
                }
                InputAction::Dec => {
                    self.change_val(self.current_value - self.step, ctx);
                }
                InputAction::ChangeByKey(key_event) => match key_event.key {
                    Key::Up | Key::NumpadAdd => {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                    Key::Down | Key::NumpadSubtract => {
                        self.change_val(self.current_value - self.step, ctx);
                    }
                    Key::Enter => {
                        if *ctx.widget().get::<bool>("lost_focus_on_activation") {
                            ctx.push_event_by_window(FocusEvent::RemoveFocus(ctx.entity));
                        }

                        ctx.push_event_strategy_by_entity(
                            ActivateEvent(ctx.entity),
                            ctx.entity,
                            EventStrategy::Direct,
                        )
                    }
                    _ => {}
                },
                InputAction::ChangeByMouseScroll(delta) => {
                    if delta.y < 0.0 {
                        self.change_val(self.current_value - self.step, ctx);
                    } else {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                }
                InputAction::Focus => {
                    self.request_focus(ctx);
                }
            }
            self.action = None;
        }
    }
}

widget!(
    /// `NumericBox` is used to let the user increase or decrease
    /// the value of the input by a given, fixed value called `step` until it reaches the upper or
    /// lower bounds.
    /// The widget can be controlled by clicking on the two control buttons, or the keybaord's
    /// Up and Down, Numpad+ and Numpad- keys, or the mouse scroll.
    /// Note: after the widget is initialized, changing the min, max or step properties has no effect.
    ///
    /// # Examples:
    /// Create a NumericBox with default values:
    /// ```rust
    /// NumericBox::new().build(ctx)
    /// ```
    ///
    /// Create a NumericBox with custom values:
    /// ```rust
    /// NumericBox::new().min(10.0).max(100.0).val(50.0).step(5.0).build(ctx)
    /// ```
    NumericBox<NumericBoxState>: ActivateHandler, ChangedHandler, KeyDownHandler {
        /// Sets or shares the background color property
        background: Brush,

        /// Sets or shares the border color property
        border_brush: Brush,

        /// Sets or shares the border width property
        border_width: Thickness,

        /// Sets or shares the border radius property
        border_radius: f64,

        /// Sets or shares the focused property
        focused: bool,

        /// Sets or shares the foreground color property
        foreground: Brush,

        /// Sets or shares the value that describes if the NumericBox should lost focus on activation (when enter pressed).
        lost_focus_on_activation: bool,

        /// Sets or shares the minimum allowed value property
        min: f64,

        /// Sets or shares the maximum allowed value property
        max: f64,

        /// Sets or shares the stepping value property
        step: f64,

        /// Sets or shares the current value property
        val: f64
    }
);

impl Template for NumericBox {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("NumericBox")
            .background("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .border_brush("#647b91")
            .border_width(1.0)
            .border_radius(3.0)
            .element("numeric_box")
            .focused(false)
            .height(32.0)
            .lost_focus_on_activation(true)
            .margin(4.0)
            .min(0.0)
            .max(200.0)
            .step(1.0)
            .val(0.0)
            .width(128.0)
            .child(
                MouseBehavior::new()
                    .on_mouse_down(move |states, _| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::Focus);
                        true
                    })
                    .on_scroll(move |states, delta| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::ChangeByMouseScroll(delta));
                        true
                    })
                    .build(ctx),
            )
            .child(
                Grid::new()
                    .columns(Columns::new().add("*").add(32.))
                    .rows(Rows::new().add(16.0).add(16.0))
                    .child(
                        TextBox::new()
                            .h_align("stretch")
                            .attach(Grid::column(0))
                            .attach(Grid::row_span(2))
                            .attach(Grid::row(0))
                            .border_brush(id)
                            .border_radius(id)
                            .border_width(id)
                            .element(ELEMENT_INPUT)
                            .enabled(false)
                            .id(ID_INPUT)
                            .max_width(96.)
                            .text("0")
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(0))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .min_width(30.0)
                            .height(30.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .text("+")
                            .margin(1)
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Inc);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(1))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .min_width(30.0)
                            .height(30.0)
                            .padding(0.0)
                            .margin(1)
                            .text("-")
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Dec);
                                true
                            })
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_key_down(move |states, event| -> bool {
                states
                    .get_mut::<NumericBoxState>(id)
                    .action(InputAction::ChangeByKey(event));
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }
}
<commit_msg>Check for equality too<commit_after>use super::behaviors::MouseBehavior;
use crate::prelude::*;
use crate::shell::{Key, KeyEvent};
use core::f64::MAX;
use rust_decimal::prelude::*;

pub static ID_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_INPUT: &'static str = "numeric_box_input";
pub static ELEMENT_BTN: &'static str = "numeric_box_button";

pub enum InputAction {
    Inc,
    Dec,
    ChangeByKey(KeyEvent),
    ChangeByMouseScroll(Point),
    Focus,
}

#[derive(Default, AsAny)]
pub struct NumericBoxState {
    pub action: Option<InputAction>,
    pub input: Entity,
    pub min: Decimal,
    pub max: Decimal,
    pub step: Decimal,
    pub current_value: Decimal,
}

impl NumericBoxState {
    fn action(&mut self, action: InputAction) {
        self.action = Some(action);
    }

    fn change_val(&mut self, new_value: Decimal, ctx: &mut Context) {
        self.current_value = self.max(self.min(new_value));
        ctx.get_widget(self.input)
            .set::<String16>("text", String16::from(self.current_value.to_string()));

        ctx.push_event_strategy_by_entity(
            ChangedEvent(ctx.entity),
            ctx.entity,
            EventStrategy::Direct,
        );
    }

    fn min(&self, d: Decimal) -> Decimal {
        if d <= self.min {
            return self.min;
        } else {
            return d;
        }
    }

    fn max(&self, d: Decimal) -> Decimal {
       if d >= self.max {
           return self.max;
       } else {
           return d;
       }
    }

    fn request_focus(&self, ctx: &mut Context) {
        if !ctx.widget().get::<bool>("focused") {
            ctx.widget().set::<bool>("focused", true);
            ctx.push_event_by_window(FocusEvent::RequestFocus(ctx.entity));
        }
    }
}

fn default_or(key: &str, default_value: f64, ctx: &mut Context) -> Decimal {
    let property = ctx.widget().clone_or_default(key);

    match Decimal::from_f64(property) {
        Some(val) => val,
        None => Decimal::from_f64(default_value).unwrap(),
    }
}

impl State for NumericBoxState {
    fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
        self.input = ctx.entity_of_child(ID_INPUT).expect(
            "NumericBoxState
        .init(): the child input could not be found!",
        );
        self.min = default_or("min", 0.0, ctx);
        self.max = default_or("max", MAX, ctx);
        self.step = default_or("step", 1.0, ctx);
        self.current_value = default_or("val", 0.0, ctx);

        let init_value = String16::from(self.current_value.to_string());
        ctx.get_widget(self.input)
            .set::<String16>("text", init_value);
    }

    // TODO: let the user type the value, or select it for cut, copy, paste operations
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        if let Some(action) = &self.action {
            match action {
                InputAction::Inc => {
                    self.change_val(self.current_value + self.step, ctx);
                }
                InputAction::Dec => {
                    self.change_val(self.current_value - self.step, ctx);
                }
                InputAction::ChangeByKey(key_event) => match key_event.key {
                    Key::Up | Key::NumpadAdd => {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                    Key::Down | Key::NumpadSubtract => {
                        self.change_val(self.current_value - self.step, ctx);
                    }
                    Key::Enter => {
                        if *ctx.widget().get::<bool>("lost_focus_on_activation") {
                            ctx.push_event_by_window(FocusEvent::RemoveFocus(ctx.entity));
                        }

                        ctx.push_event_strategy_by_entity(
                            ActivateEvent(ctx.entity),
                            ctx.entity,
                            EventStrategy::Direct,
                        )
                    }
                    _ => {}
                },
                InputAction::ChangeByMouseScroll(delta) => {
                    if delta.y < 0.0 {
                        self.change_val(self.current_value - self.step, ctx);
                    } else {
                        self.change_val(self.current_value + self.step, ctx);
                    }
                }
                InputAction::Focus => {
                    self.request_focus(ctx);
                }
            }
            self.action = None;
        }
    }
}

widget!(
    /// `NumericBox` is used to let the user increase or decrease
    /// the value of the input by a given, fixed value called `step` until it reaches the upper or
    /// lower bounds.
    /// The widget can be controlled by clicking on the two control buttons, or the keybaord's
    /// Up and Down, Numpad+ and Numpad- keys, or the mouse scroll.
    /// Note: after the widget is initialized, changing the min, max or step properties has no effect.
    ///
    /// # Examples:
    /// Create a NumericBox with default values:
    /// ```rust
    /// NumericBox::new().build(ctx)
    /// ```
    ///
    /// Create a NumericBox with custom values:
    /// ```rust
    /// NumericBox::new().min(10.0).max(100.0).val(50.0).step(5.0).build(ctx)
    /// ```
    NumericBox<NumericBoxState>: ActivateHandler, ChangedHandler, KeyDownHandler {
        /// Sets or shares the background color property
        background: Brush,

        /// Sets or shares the border color property
        border_brush: Brush,

        /// Sets or shares the border width property
        border_width: Thickness,

        /// Sets or shares the border radius property
        border_radius: f64,

        /// Sets or shares the focused property
        focused: bool,

        /// Sets or shares the foreground color property
        foreground: Brush,

        /// Sets or shares the value that describes if the NumericBox should lost focus on activation (when enter pressed).
        lost_focus_on_activation: bool,

        /// Sets or shares the minimum allowed value property
        min: f64,

        /// Sets or shares the maximum allowed value property
        max: f64,

        /// Sets or shares the stepping value property
        step: f64,

        /// Sets or shares the current value property
        val: f64
    }
);

impl Template for NumericBox {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        self.name("NumericBox")
            .background("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .border_brush("#647b91")
            .border_width(1.0)
            .border_radius(3.0)
            .element("numeric_box")
            .focused(false)
            .height(32.0)
            .lost_focus_on_activation(true)
            .margin(4.0)
            .min(0.0)
            .max(200.0)
            .step(1.0)
            .val(0.0)
            .width(128.0)
            .child(
                MouseBehavior::new()
                    .on_mouse_down(move |states, _| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::Focus);
                        true
                    })
                    .on_scroll(move |states, delta| {
                        states
                            .get_mut::<NumericBoxState>(id)
                            .action(InputAction::ChangeByMouseScroll(delta));
                        true
                    })
                    .build(ctx),
            )
            .child(
                Grid::new()
                    .columns(Columns::new().add("*").add(32.))
                    .rows(Rows::new().add(16.0).add(16.0))
                    .child(
                        TextBox::new()
                            .h_align("stretch")
                            .attach(Grid::column(0))
                            .attach(Grid::row_span(2))
                            .attach(Grid::row(0))
                            .border_brush(id)
                            .border_radius(id)
                            .border_width(id)
                            .element(ELEMENT_INPUT)
                            .enabled(false)
                            .id(ID_INPUT)
                            .max_width(96.)
                            .text("0")
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(0))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .min_width(30.0)
                            .height(30.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .text("+")
                            .margin(1)
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Inc);
                                true
                            })
                            .build(ctx),
                    )
                    .child(
                        Button::new()
                            .attach(Grid::column(1))
                            .attach(Grid::row(1))
                            .border_brush("transparent")
                            .border_radius(0.0)
                            .border_width(0.0)
                            .class("single_content")
                            .element(ELEMENT_BTN)
                            .min_width(30.0)
                            .height(30.0)
                            .padding(0.0)
                            .margin(1)
                            .text("-")
                            .on_click(move |states, _| {
                                states
                                    .get_mut::<NumericBoxState>(id)
                                    .action(InputAction::Dec);
                                true
                            })
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_key_down(move |states, event| -> bool {
                states
                    .get_mut::<NumericBoxState>(id)
                    .action(InputAction::ChangeByKey(event));
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{
    path::{Path, PathBuf},
    sync::Arc,
};

use crossbeam_channel::{unbounded, Receiver};
use lsp_server::ErrorCode;
use lsp_types::Url;
use parking_lot::RwLock;
use ra_ide::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
    SourceRootId,
};
use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
use relative_path::RelativePathBuf;
use std::path::{Component, Prefix};

use crate::{
    main_loop::pending_requests::{CompletedRequest, LatestRequests},
    LspError, Result,
};
use std::str::FromStr;

#[derive(Debug, Clone)]
pub struct Options {
    pub publish_decorations: bool,
    pub supports_location_link: bool,
    pub line_folding_only: bool,
    pub max_inlay_hint_length: Option<usize>,
}

/// `WorldState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
#[derive(Debug)]
pub struct WorldState {
    pub options: Options,
    //FIXME: this belongs to `LoopState` rather than to `WorldState`
    pub roots_to_scan: usize,
    pub roots: Vec<PathBuf>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis_host: AnalysisHost,
    pub vfs: Arc<RwLock<Vfs>>,
    pub task_receiver: Receiver<VfsTask>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
}

/// An immutable snapshot of the world's state at a point in time.
pub struct WorldSnapshot {
    pub options: Options,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis: Analysis,
    pub vfs: Arc<RwLock<Vfs>>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
}

impl WorldState {
    pub fn new(
        folder_roots: Vec<PathBuf>,
        workspaces: Vec<ProjectWorkspace>,
        lru_capacity: Option<usize>,
        exclude_globs: &[Glob],
        watch: Watch,
        options: Options,
        feature_flags: FeatureFlags,
    ) -> WorldState {
        let mut change = AnalysisChange::new();

        let mut roots = Vec::new();
        roots.extend(folder_roots.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(true);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(path.clone(), filter.into_vfs_filter())
        }));
        for ws in workspaces.iter() {
            roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
                let mut filter =
                    RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
                for glob in exclude_globs.iter() {
                    filter = filter.exclude(glob.clone());
                }
                RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
            }));
        }
        let (task_sender, task_receiver) = unbounded();
        let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
        let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
        let roots_to_scan = vfs_roots.len();
        for r in vfs_roots {
            let vfs_root_path = vfs.root2path(r);
            let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
            change.add_root(SourceRootId(r.0), is_local);
            change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
        }

        // FIXME: Read default cfgs from config
        let default_cfg_options = {
            let mut opts = get_rustc_cfg_options();
            opts.insert_atom("test".into());
            opts.insert_atom("debug_assertion".into());
            opts
        };

        // Create crate graph from all the workspaces
        let mut crate_graph = CrateGraph::default();
        let mut load = |path: &std::path::Path| {
            let vfs_file = vfs.load(path);
            vfs_file.map(|f| FileId(f.0))
        };
        for ws in workspaces.iter() {
            let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load);
            let shift = crate_graph.extend(graph);
            for (crate_id, name) in crate_names {
                change.set_debug_crate_name(crate_id.shift(shift), name)
            }
        }
        change.set_crate_graph(crate_graph);

        let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
        analysis_host.apply_change(change);
        WorldState {
            options,
            roots_to_scan,
            roots: folder_roots,
            workspaces: Arc::new(workspaces),
            analysis_host,
            vfs: Arc::new(RwLock::new(vfs)),
            task_receiver,
            latest_requests: Default::default(),
        }
    }

    /// Returns a vec of libraries
    /// FIXME: better API here
    pub fn process_changes(
        &mut self,
    ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> {
        let changes = self.vfs.write().commit_changes();
        if changes.is_empty() {
            return Vec::new();
        }
        let mut libs = Vec::new();
        let mut change = AnalysisChange::new();
        for c in changes {
            match c {
                VfsChange::AddRoot { root, files } => {
                    let root_path = self.vfs.read().root2path(root);
                    let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
                    if is_local {
                        self.roots_to_scan -= 1;
                        for (file, path, text) in files {
                            change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                        }
                    } else {
                        let files = files
                            .into_iter()
                            .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
                            .collect();
                        libs.push((SourceRootId(root.0), files));
                    }
                }
                VfsChange::AddFile { root, file, path, text } => {
                    change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                }
                VfsChange::RemoveFile { root, file, path } => {
                    change.remove_file(SourceRootId(root.0), FileId(file.0), path)
                }
                VfsChange::ChangeFile { file, text } => {
                    change.change_file(FileId(file.0), text);
                }
            }
        }
        self.analysis_host.apply_change(change);
        libs
    }

    pub fn add_lib(&mut self, data: LibraryData) {
        self.roots_to_scan -= 1;
        let mut change = AnalysisChange::new();
        change.add_library(data);
        self.analysis_host.apply_change(change);
    }

    pub fn snapshot(&self) -> WorldSnapshot {
        WorldSnapshot {
            options: self.options.clone(),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            latest_requests: Arc::clone(&self.latest_requests),
        }
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.analysis_host.maybe_collect_garbage()
    }

    pub fn collect_garbage(&mut self) {
        self.analysis_host.collect_garbage()
    }

    pub fn complete_request(&mut self, request: CompletedRequest) {
        self.latest_requests.write().record(request)
    }

    pub fn feature_flags(&self) -> &FeatureFlags {
        self.analysis_host.feature_flags()
    }
}

impl WorldSnapshot {
    pub fn analysis(&self) -> &Analysis {
        &self.analysis
    }

    pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
        let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
        let file = self.vfs.read().path2file(&path).ok_or_else(|| {
            // Show warning as this file is outside current workspace
            LspError {
                code: ErrorCode::InvalidRequest as i32,
                message: "Rust file outside current workspace is not supported yet.".to_string(),
            }
        })?;
        Ok(FileId(file.0))
    }

    pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
        let path = self.vfs.read().file2path(VfsFile(id.0));
        let url = url_from_path_with_drive_lowercasing(path)?;

        Ok(url)
    }

    pub fn file_line_endings(&self, id: FileId) -> LineEndings {
        self.vfs.read().file_line_endings(VfsFile(id.0))
    }

    pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
        let base = self.vfs.read().root2path(VfsRoot(root.0));
        let path = path.to_path(base);
        let url = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
        Ok(url)
    }

    pub fn status(&self) -> String {
        let mut res = String::new();
        if self.workspaces.is_empty() {
            res.push_str("no workspaces\n")
        } else {
            res.push_str("workspaces:\n");
            for w in self.workspaces.iter() {
                res += &format!("{} packages loaded\n", w.n_packages());
            }
        }
        res.push_str("\nanalysis:\n");
        res.push_str(
            &self
                .analysis
                .status()
                .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
        );
        res
    }

    pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
        let path = self.vfs.read().file2path(VfsFile(file_id.0));
        self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
    }

    pub fn feature_flags(&self) -> &FeatureFlags {
        self.analysis.feature_flags()
    }
}

/// Returns a `Url` object from a given path, will lowercase drive letters if present.
/// This will only happen when processing windows paths.
///
/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
    let component_has_windows_drive = path
        .as_ref()
        .components()
        .find(|comp| {
            if let Component::Prefix(c) = comp {
                match c.kind() {
                    Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
                    _ => return false,
                }
            }
            false
        })
        .is_some();

    // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
    if component_has_windows_drive {
        let url_original = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;

        let drive_partition: Vec<&str> =
            url_original.as_str().rsplitn(2, ':').collect::<Vec<&str>>();

        // There is a drive partition, but we never found a colon.
        // This should not happen, but in this case we just pass it through.
        if drive_partition.len() == 1 {
            return Ok(url_original);
        }

        let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
        let url = Url::from_str(&joined).expect("This came from a valid `Url`");

        Ok(url)
    } else {
        Ok(Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
    }
}

// `Url` is not able to parse windows paths on unix machines.
#[cfg(target_os = "windows")]
#[cfg(test)]
mod path_conversion_windows_tests {
    use super::url_from_path_with_drive_lowercasing;
    #[test]
    fn test_lowercase_drive_letter_with_drive() {
        let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();

        assert_eq!(url.to_string(), "file:///c:/Test");
    }

    #[test]
    fn test_drive_without_colon_passthrough() {
        let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();

        assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
    }
}
<commit_msg>remove unnecessary turbofish.<commit_after>//! FIXME: write short doc here

use std::{
    path::{Path, PathBuf},
    sync::Arc,
};

use crossbeam_channel::{unbounded, Receiver};
use lsp_server::ErrorCode;
use lsp_types::Url;
use parking_lot::RwLock;
use ra_ide::{
    Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
    SourceRootId,
};
use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
use relative_path::RelativePathBuf;
use std::path::{Component, Prefix};

use crate::{
    main_loop::pending_requests::{CompletedRequest, LatestRequests},
    LspError, Result,
};
use std::str::FromStr;

#[derive(Debug, Clone)]
pub struct Options {
    pub publish_decorations: bool,
    pub supports_location_link: bool,
    pub line_folding_only: bool,
    pub max_inlay_hint_length: Option<usize>,
}

/// `WorldState` is the primary mutable state of the language server
///
/// The most interesting components are `vfs`, which stores a consistent
/// snapshot of the file systems, and `analysis_host`, which stores our
/// incremental salsa database.
#[derive(Debug)]
pub struct WorldState {
    pub options: Options,
    //FIXME: this belongs to `LoopState` rather than to `WorldState`
    pub roots_to_scan: usize,
    pub roots: Vec<PathBuf>,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis_host: AnalysisHost,
    pub vfs: Arc<RwLock<Vfs>>,
    pub task_receiver: Receiver<VfsTask>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
}

/// An immutable snapshot of the world's state at a point in time.
pub struct WorldSnapshot {
    pub options: Options,
    pub workspaces: Arc<Vec<ProjectWorkspace>>,
    pub analysis: Analysis,
    pub vfs: Arc<RwLock<Vfs>>,
    pub latest_requests: Arc<RwLock<LatestRequests>>,
}

impl WorldState {
    pub fn new(
        folder_roots: Vec<PathBuf>,
        workspaces: Vec<ProjectWorkspace>,
        lru_capacity: Option<usize>,
        exclude_globs: &[Glob],
        watch: Watch,
        options: Options,
        feature_flags: FeatureFlags,
    ) -> WorldState {
        let mut change = AnalysisChange::new();

        let mut roots = Vec::new();
        roots.extend(folder_roots.iter().map(|path| {
            let mut filter = RustPackageFilterBuilder::default().set_member(true);
            for glob in exclude_globs.iter() {
                filter = filter.exclude(glob.clone());
            }
            RootEntry::new(path.clone(), filter.into_vfs_filter())
        }));
        for ws in workspaces.iter() {
            roots.extend(ws.to_roots().into_iter().map(|pkg_root| {
                let mut filter =
                    RustPackageFilterBuilder::default().set_member(pkg_root.is_member());
                for glob in exclude_globs.iter() {
                    filter = filter.exclude(glob.clone());
                }
                RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
            }));
        }
        let (task_sender, task_receiver) = unbounded();
        let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
        let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
        let roots_to_scan = vfs_roots.len();
        for r in vfs_roots {
            let vfs_root_path = vfs.root2path(r);
            let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
            change.add_root(SourceRootId(r.0), is_local);
            change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
        }

        // FIXME: Read default cfgs from config
        let default_cfg_options = {
            let mut opts = get_rustc_cfg_options();
            opts.insert_atom("test".into());
            opts.insert_atom("debug_assertion".into());
            opts
        };

        // Create crate graph from all the workspaces
        let mut crate_graph = CrateGraph::default();
        let mut load = |path: &std::path::Path| {
            let vfs_file = vfs.load(path);
            vfs_file.map(|f| FileId(f.0))
        };
        for ws in workspaces.iter() {
            let (graph, crate_names) = ws.to_crate_graph(&default_cfg_options, &mut load);
            let shift = crate_graph.extend(graph);
            for (crate_id, name) in crate_names {
                change.set_debug_crate_name(crate_id.shift(shift), name)
            }
        }
        change.set_crate_graph(crate_graph);

        let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
        analysis_host.apply_change(change);
        WorldState {
            options,
            roots_to_scan,
            roots: folder_roots,
            workspaces: Arc::new(workspaces),
            analysis_host,
            vfs: Arc::new(RwLock::new(vfs)),
            task_receiver,
            latest_requests: Default::default(),
        }
    }

    /// Returns a vec of libraries
    /// FIXME: better API here
    pub fn process_changes(
        &mut self,
    ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> {
        let changes = self.vfs.write().commit_changes();
        if changes.is_empty() {
            return Vec::new();
        }
        let mut libs = Vec::new();
        let mut change = AnalysisChange::new();
        for c in changes {
            match c {
                VfsChange::AddRoot { root, files } => {
                    let root_path = self.vfs.read().root2path(root);
                    let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
                    if is_local {
                        self.roots_to_scan -= 1;
                        for (file, path, text) in files {
                            change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                        }
                    } else {
                        let files = files
                            .into_iter()
                            .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
                            .collect();
                        libs.push((SourceRootId(root.0), files));
                    }
                }
                VfsChange::AddFile { root, file, path, text } => {
                    change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
                }
                VfsChange::RemoveFile { root, file, path } => {
                    change.remove_file(SourceRootId(root.0), FileId(file.0), path)
                }
                VfsChange::ChangeFile { file, text } => {
                    change.change_file(FileId(file.0), text);
                }
            }
        }
        self.analysis_host.apply_change(change);
        libs
    }

    pub fn add_lib(&mut self, data: LibraryData) {
        self.roots_to_scan -= 1;
        let mut change = AnalysisChange::new();
        change.add_library(data);
        self.analysis_host.apply_change(change);
    }

    pub fn snapshot(&self) -> WorldSnapshot {
        WorldSnapshot {
            options: self.options.clone(),
            workspaces: Arc::clone(&self.workspaces),
            analysis: self.analysis_host.analysis(),
            vfs: Arc::clone(&self.vfs),
            latest_requests: Arc::clone(&self.latest_requests),
        }
    }

    pub fn maybe_collect_garbage(&mut self) {
        self.analysis_host.maybe_collect_garbage()
    }

    pub fn collect_garbage(&mut self) {
        self.analysis_host.collect_garbage()
    }

    pub fn complete_request(&mut self, request: CompletedRequest) {
        self.latest_requests.write().record(request)
    }

    pub fn feature_flags(&self) -> &FeatureFlags {
        self.analysis_host.feature_flags()
    }
}

impl WorldSnapshot {
    pub fn analysis(&self) -> &Analysis {
        &self.analysis
    }

    pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
        let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
        let file = self.vfs.read().path2file(&path).ok_or_else(|| {
            // Show warning as this file is outside current workspace
            LspError {
                code: ErrorCode::InvalidRequest as i32,
                message: "Rust file outside current workspace is not supported yet.".to_string(),
            }
        })?;
        Ok(FileId(file.0))
    }

    pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
        let path = self.vfs.read().file2path(VfsFile(id.0));
        let url = url_from_path_with_drive_lowercasing(path)?;

        Ok(url)
    }

    pub fn file_line_endings(&self, id: FileId) -> LineEndings {
        self.vfs.read().file_line_endings(VfsFile(id.0))
    }

    pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
        let base = self.vfs.read().root2path(VfsRoot(root.0));
        let path = path.to_path(base);
        let url = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
        Ok(url)
    }

    pub fn status(&self) -> String {
        let mut res = String::new();
        if self.workspaces.is_empty() {
            res.push_str("no workspaces\n")
        } else {
            res.push_str("workspaces:\n");
            for w in self.workspaces.iter() {
                res += &format!("{} packages loaded\n", w.n_packages());
            }
        }
        res.push_str("\nanalysis:\n");
        res.push_str(
            &self
                .analysis
                .status()
                .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
        );
        res
    }

    pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
        let path = self.vfs.read().file2path(VfsFile(file_id.0));
        self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
    }

    pub fn feature_flags(&self) -> &FeatureFlags {
        self.analysis.feature_flags()
    }
}

/// Returns a `Url` object from a given path, will lowercase drive letters if present.
/// This will only happen when processing windows paths.
///
/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
    let component_has_windows_drive = path
        .as_ref()
        .components()
        .find(|comp| {
            if let Component::Prefix(c) = comp {
                match c.kind() {
                    Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
                    _ => return false,
                }
            }
            false
        })
        .is_some();

    // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
    if component_has_windows_drive {
        let url_original = Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;

        let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();

        // There is a drive partition, but we never found a colon.
        // This should not happen, but in this case we just pass it through.
        if drive_partition.len() == 1 {
            return Ok(url_original);
        }

        let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
        let url = Url::from_str(&joined).expect("This came from a valid `Url`");

        Ok(url)
    } else {
        Ok(Url::from_file_path(&path)
            .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
    }
}

// `Url` is not able to parse windows paths on unix machines.
#[cfg(target_os = "windows")]
#[cfg(test)]
mod path_conversion_windows_tests {
    use super::url_from_path_with_drive_lowercasing;
    #[test]
    fn test_lowercase_drive_letter_with_drive() {
        let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();

        assert_eq!(url.to_string(), "file:///c:/Test");
    }

    #[test]
    fn test_drive_without_colon_passthrough() {
        let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();

        assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
    }
}
<|endoftext|>