<commit_before>use crate::controls::ControlHandle;
use super::base_helper::{to_utf16};
use winapi::um::winuser::{CF_BITMAP, CF_TEXT, CF_UNICODETEXT};
use winapi::um::winnt::HANDLE;

#[derive(Copy, Clone)]
pub enum ClipboardFormat {
    /// ANSI text. You probably want to use `UnicodeText`.
    Text,

    /// UnicodeText. Equivalent to a OsString
    UnicodeText,

    /// A bitmap file
    Bitmap,

    /// Global clipboard format to share data between applications
    /// The format name comparison is case-insensitive.
    Global(&'static str)
}

impl ClipboardFormat {

    fn into_raw(&self) -> u32 {
        use ClipboardFormat::*;
        use winapi::um::winuser::RegisterClipboardFormatW;

        match self {
            Text => CF_TEXT,
            UnicodeText => CF_UNICODETEXT,
            Bitmap => CF_BITMAP,
            Global(v) => unsafe {
                let v = to_utf16(v);
                RegisterClipboardFormatW(v.as_ptr())
            }
        }
    }

}

/// Wrapper over a clipboard global allocation handle.
/// This value should be released with `release` or dropped before closing the clipboard.
pub struct ClipboardData(HANDLE);

impl ClipboardData {
    pub unsafe fn cast<D: Copy>(&self) -> *const D { self.0 as *const D }
    pub fn release(self) { /* See drop implementation */ }
}

impl Drop for ClipboardData {
    fn drop(&mut self) {
        unsafe { ::winapi::um::winbase::GlobalUnlock(self.0); }
    }
}

/**
A global object that wraps the system clipboard. It can be used to set or get the system cliboard content.

It's important to keep in mind that there is no way to validate data sent through the clipboard API, as such this wrapper
is still mostly unsafe and you must validate the data when reading.

Writing / Reading text

```rust
use native_windows_gui as nwg;

fn clipboard_text(window: &nwg::Window) {
    nwg::Clipboard::set_data_text(window, "Hello!");

    let text = nwg::Clipboard::data_text(window);
    assert!(text.is_some());
    assert!(&text.unwrap() == &"Hello!");
}
```

Writing / Reading custom data

```rust
use native_windows_gui as nwg;

#[repr(C)]
#[derive(Clone, Copy)]
struct Hello {
    foo: usize,
    bar: [u16; 3]
}

fn write_custom_data(window: &nwg::Window) {
    let data = Hello {
        foo: 6529,
        bar: [0, 100, 20]
    };

    nwg::Clipboard::open(window);
    nwg::Clipboard::empty();
    unsafe {
        nwg::Clipboard::set_data(
            nwg::ClipboardFormat::Global("Hello"),
            &data as *const Hello,
            1
        );
    }

    nwg::Clipboard::close();
}

fn read_custom_data(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let data = nwg::Clipboard::data(nwg::ClipboardFormat::Global("Hello"));
        nwg::Clipboard::close();
        data
    }
}

fn read_custom_data_handle(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let handle = nwg::Clipboard::data_handle(nwg::ClipboardFormat::Global("Hello"));
        let data = match handle {
            Some(h) => {
                let data_ptr: *const Hello = h.cast();
                let data = *data_ptr;
                h.release();
                Some(data)
            },
            None => None
        };

        nwg::Clipboard::close();
        data
    }
}

```
*/
pub struct Clipboard;

impl Clipboard {

    /**
        Fill the clipboard with the selected text.
        The data use the `ClipboardFormat::UnicodeText` format.

        This is a high level function that handles `open` and `close`
    */
    pub fn set_data_text<'a, C: Into<ControlHandle>>(handle: C, text: &'a str) {
        Clipboard::open(handle);
        Clipboard::empty();

        let text = to_utf16(text);
        unsafe { Clipboard::set_data(ClipboardFormat::UnicodeText, text.as_ptr(), text.len()) };

        Clipboard::close();
    }

    /**
        Return the current text value in the clipboard (if there is one).
        This function will return the text if the clipboard has either the `UnicodeText` format or the `Text` format.

        If the clipboard do not have a text format OR the text data is not a valid utf-8 sequence, this function will return `None`.
    */
    pub fn data_text<C: Into<ControlHandle>>(handle: C) -> Option<String> {
        use ClipboardFormat::*;
        let mut data = None;

        Clipboard::open(handle);

        unsafe {
            if Clipboard::has_format(UnicodeText) {
                let handle = Clipboard::data_handle(UnicodeText).unwrap();
                data = from_wide_ptr(handle.cast());
                handle.release();
            } else if Clipboard::has_format(Text) {
                let handle = Clipboard::data_handle(Text).unwrap();
                data = from_ptr(handle.cast());
                handle.release();
            }
        }

        Clipboard::close();

        data
    }

    /**
        Remove the current data in the clipboard
    */
    pub fn clear<C: Into<ControlHandle>>() {
        use winapi::um::winuser::{OpenClipboard, EmptyClipboard, CloseClipboard};
        use std::ptr;
        unsafe {
            OpenClipboard(ptr::null_mut());
            EmptyClipboard();
            CloseClipboard();
        }
    }

    /**
        Opens the clipboard for examination and prevents other applications from modifying the clipboard content.
        Another call to `close` should be made as soon as the application is done with the clipboard.

        Parameters:
            handle: A window control that will be identified as the current "owner" of the clipboard

        This function will panic if the control is not HWND based.
    */
    pub fn open<C: Into<ControlHandle>>(handle: C) {
        use winapi::um::winuser::OpenClipboard;
        let handle = handle.into().hwnd().expect("Control should be a window");
        unsafe { OpenClipboard(handle); }
    }

    /**
        Places data on the clipboard in a specified clipboard format.

        This method is unsafe because there is no way to ensure that data is valid.
        If possible, it is recommended to use a higher level function such as `set_data_text` instead.

        If the data will be used across applications, make sure that D has `repr(C)` for compatibility.

        The clipboard must be open when calling this function.

        * Note 1: `data` is copied into a global system allocation. It's ok to discard the data as soon as this function returns.
        * Note 2: When copying text, the null byte must be included.
    */
    pub unsafe fn set_data<D: Copy>(fmt: ClipboardFormat, data: *const D, count: usize) {
        use winapi::um::winuser::SetClipboardData;
        use winapi::um::winbase::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE};
        use winapi::shared::basetsd::SIZE_T;
        use std::{mem, ptr};

        let fmt = fmt.into_raw();
        let alloc_size = (mem::size_of::<D>() * count) as SIZE_T;
        let alloc = GlobalAlloc(GMEM_MOVEABLE, alloc_size);

        ptr::copy_nonoverlapping(data, GlobalLock(alloc) as *mut D, count);
        GlobalUnlock(alloc);

        SetClipboardData(fmt, alloc as HANDLE);
    }

    /**
        Check if the selected format is available in the clipboard.
        The clipboard must be open when calling this function.
    */
    pub fn has_format(fmt: ClipboardFormat) -> bool {
        use winapi::um::winuser::EnumClipboardFormats;

        let selected_format = fmt.into_raw();
        let mut format = 0;
        let next_format = unsafe {
            |fmt: &mut u32| { let f = EnumClipboardFormats(*fmt); *fmt = f; f }
        };

        let mut found = false;
        while next_format(&mut format) != 0 {
            if format == selected_format {
                found = true;
                break;
            }
        }

        found
    }

    /**
        Get the handle to the clipboard data and copy it's data into a new value of type `D`.
        This function is very unsafe because it assumes that the handle points to the correct type.

        The clipboard must be open when calling this function.

        If no data is found with the selected clipboard format, `None` is returned.
    */
    pub unsafe fn data<D: Copy>(fmt: ClipboardFormat) -> Option<D> {
        use winapi::um::winuser::GetClipboardData;
        use winapi::um::winbase::{GlobalLock, GlobalUnlock};
        use std::{ptr, mem};

        let fmt = fmt.into_raw();
        let handle = GetClipboardData(fmt);
        if handle.is_null() {
            return None;
        }

        let mut data = mem::zeroed();
        ptr::copy_nonoverlapping(GlobalLock(handle) as *const D, &mut data, 1);
        GlobalUnlock(handle);

        Some(data)
    }

    /**
        Gets the data handle of the clipboard, lock the memory and return it in a `ClipboardData` wrapper.
        The returned data is read-only. The application should copy the data and release the handle as soon as possible.

        The clipboard must be open when calling this function.

        If no data is found with the selected clipboard format, `None` is returned.
    */
    pub unsafe fn data_handle(fmt: ClipboardFormat) -> Option<ClipboardData> {
        use winapi::um::winuser::GetClipboardData;
        use winapi::um::winbase::GlobalLock;

        let fmt = fmt.into_raw();
        let handle = GetClipboardData(fmt);
        match handle.is_null() {
            true => None,
            false => Some(ClipboardData(GlobalLock(handle)))
        }
    }

    /**
        A window can place more than one clipboard object on the clipboard, each representing the same information in a different clipboard format.
        Retrieves the number of different data formats currently on the clipboard.
    */
    pub fn count_clipboard_formats() -> u32 {
        use winapi::um::winuser::CountClipboardFormats;
        unsafe { CountClipboardFormats() as u32 }
    }

    /**
        Empty the clipboard data.
        This is a low-level function and `open` must have been called first.
        To only clear the clipboard data use `clear`
    */
    pub fn empty() {
        use winapi::um::winuser::EmptyClipboard;
        unsafe { EmptyClipboard(); }
    }

    /**
        Close the clipboard after it was opened with the `open` function.
    */
    pub fn close() {
        use winapi::um::winuser::CloseClipboard;
        unsafe { CloseClipboard(); }
    }

    /**
        Return the handle of the window that owns the clipboard
    */
    pub fn ownder() -> ControlHandle {
        let handle = unsafe { ::winapi::um::winuser::GetClipboardOwner() };
        ControlHandle::Hwnd(handle)
    }

}

unsafe fn from_wide_ptr(ptr: *const u16) -> Option<String> {
    use std::slice::from_raw_parts;
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt;

    let mut length: isize = 0;
    while *&*ptr.offset(length) != 0 {
        length += 1;
    }

    let array: &[u16] = from_raw_parts(ptr, length as usize);

    OsString::from_wide(&array)
        .into_string()
        .ok()
}

unsafe fn from_ptr(ptr: *const u8) -> Option<String> {
    use std::slice::from_raw_parts;
    use std::str;

    let mut length: isize = 0;
    while *&*ptr.offset(length) != 0 {
        length += 1;
    }

    let array: &[u8] = from_raw_parts(ptr, length as usize);

    str::from_utf8(array)
        .map(|s| s.into())
        .ok()
}
<commit_msg>Use IsClipboardFormatAvailable and dealloc on set fail<commit_after>use crate::controls::ControlHandle;
use super::base_helper::{to_utf16};
use winapi::um::winuser::{CF_BITMAP, CF_TEXT, CF_UNICODETEXT};
use winapi::um::winnt::HANDLE;

#[derive(Copy, Clone)]
pub enum ClipboardFormat {
    /// ANSI text. You probably want to use `UnicodeText`.
    Text,

    /// UnicodeText. Equivalent to a OsString
    UnicodeText,

    /// A bitmap file
    Bitmap,

    /// Global clipboard format to share data between applications
    /// The format name comparison is case-insensitive.
    Global(&'static str)
}

impl ClipboardFormat {

    fn into_raw(&self) -> u32 {
        use ClipboardFormat::*;
        use winapi::um::winuser::RegisterClipboardFormatW;

        match self {
            Text => CF_TEXT,
            UnicodeText => CF_UNICODETEXT,
            Bitmap => CF_BITMAP,
            Global(v) => unsafe {
                let v = to_utf16(v);
                RegisterClipboardFormatW(v.as_ptr())
            }
        }
    }

}

/// Wrapper over a clipboard global allocation handle.
/// This value should be released with `release` or dropped before closing the clipboard.
pub struct ClipboardData(HANDLE);

impl ClipboardData {
    pub unsafe fn cast<D: Copy>(&self) -> *const D { self.0 as *const D }
    pub fn release(self) { /* See drop implementation */ }
}

impl Drop for ClipboardData {
    fn drop(&mut self) {
        unsafe { ::winapi::um::winbase::GlobalUnlock(self.0); }
    }
}

/**
A global object that wraps the system clipboard. It can be used to set or get the system cliboard content.

It's important to keep in mind that there is no way to validate data sent through the clipboard API, as such this wrapper
is still mostly unsafe and you must validate the data when reading.

Writing / Reading text

```rust
use native_windows_gui as nwg;

fn clipboard_text(window: &nwg::Window) {
    nwg::Clipboard::set_data_text(window, "Hello!");

    let text = nwg::Clipboard::data_text(window);
    assert!(text.is_some());
    assert!(&text.unwrap() == &"Hello!");
}
```

Writing / Reading custom data

```rust
use native_windows_gui as nwg;

#[repr(C)]
#[derive(Clone, Copy)]
struct Hello {
    foo: usize,
    bar: [u16; 3]
}

fn write_custom_data(window: &nwg::Window) {
    let data = Hello {
        foo: 6529,
        bar: [0, 100, 20]
    };

    nwg::Clipboard::open(window);
    nwg::Clipboard::empty();
    unsafe {
        nwg::Clipboard::set_data(
            nwg::ClipboardFormat::Global("Hello"),
            &data as *const Hello,
            1
        );
    }

    nwg::Clipboard::close();
}

fn read_custom_data(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let data = nwg::Clipboard::data(nwg::ClipboardFormat::Global("Hello"));
        nwg::Clipboard::close();
        data
    }
}

fn read_custom_data_handle(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let handle = nwg::Clipboard::data_handle(nwg::ClipboardFormat::Global("Hello"));
        let data = match handle {
            Some(h) => {
                let data_ptr: *const Hello = h.cast();
                let data = *data_ptr;
                h.release();
                Some(data)
            },
            None => None
        };

        nwg::Clipboard::close();
        data
    }
}

```
*/
pub struct Clipboard;

impl Clipboard {

    /**
        Fill the clipboard with the selected text.
        The data use the `ClipboardFormat::UnicodeText` format.

        This is a high level function that handles `open` and `close`
    */
    pub fn set_data_text<'a, C: Into<ControlHandle>>(handle: C, text: &'a str) {
        Clipboard::open(handle);
        Clipboard::empty();

        let text = to_utf16(text);
        unsafe { Clipboard::set_data(ClipboardFormat::UnicodeText, text.as_ptr(), text.len()) };

        Clipboard::close();
    }

    /**
        Return the current text value in the clipboard (if there is one).
        This function will return the text if the clipboard has either the `UnicodeText` format or the `Text` format.

        If the clipboard do not have a text format OR the text data is not a valid utf-8 sequence, this function will return `None`.
    */
    pub fn data_text<C: Into<ControlHandle>>(handle: C) -> Option<String> {
        use ClipboardFormat::*;
        let mut data = None;

        Clipboard::open(handle);

        unsafe {
            if Clipboard::has_format(UnicodeText) {
                let handle = Clipboard::data_handle(UnicodeText).unwrap();
                data = from_wide_ptr(handle.cast());
                handle.release();
            } else if Clipboard::has_format(Text) {
                let handle = Clipboard::data_handle(Text).unwrap();
                data = from_ptr(handle.cast());
                handle.release();
            }
        }

        Clipboard::close();

        data
    }

    /**
        Remove the current data in the clipboard
    */
    pub fn clear<C: Into<ControlHandle>>() {
        use winapi::um::winuser::{OpenClipboard, EmptyClipboard, CloseClipboard};
        use std::ptr;
        unsafe {
            OpenClipboard(ptr::null_mut());
            EmptyClipboard();
            CloseClipboard();
        }
    }

    /**
        Opens the clipboard for examination and prevents other applications from modifying the clipboard content.
        Another call to `close` should be made as soon as the application is done with the clipboard.

        Parameters:
            handle: A window control that will be identified as the current "owner" of the clipboard

        This function will panic if the control is not HWND based.
    */
    pub fn open<C: Into<ControlHandle>>(handle: C) {
        use winapi::um::winuser::OpenClipboard;
        let handle = handle.into().hwnd().expect("Control should be a window");
        unsafe { OpenClipboard(handle); }
    }

    /**
        Places data on the clipboard in a specified clipboard format.

        This method is unsafe because there is no way to ensure that data is valid.
        If possible, it is recommended to use a higher level function such as `set_data_text` instead.

        If the data will be used across applications, make sure that D has `repr(C)` for compatibility.

        The clipboard must be open when calling this function.

        * Note 1: `data` is copied into a global system allocation. It's ok to discard the data as soon as this function returns.
        * Note 2: When copying text, the null byte must be included.
    */
    pub unsafe fn set_data<D: Copy>(fmt: ClipboardFormat, data: *const D, count: usize) {
        use winapi::um::winuser::SetClipboardData;
        use winapi::um::winbase::{GlobalAlloc, GlobalLock, GlobalFree, GlobalUnlock, GMEM_MOVEABLE};
        use winapi::shared::basetsd::SIZE_T;
        use std::{mem, ptr};

        let fmt = fmt.into_raw();
        let alloc_size = (mem::size_of::<D>() * count) as SIZE_T;
        let alloc = GlobalAlloc(GMEM_MOVEABLE, alloc_size);

        ptr::copy_nonoverlapping(data, GlobalLock(alloc) as *mut D, count);
        GlobalUnlock(alloc);

        if SetClipboardData(fmt, alloc as HANDLE).is_null() {
            GlobalFree(alloc);
        }
    }

    /**
        Check if the selected format is available in the clipboard.
    */
    pub fn has_format(fmt: ClipboardFormat) -> bool {
        use winapi::um::winuser::IsClipboardFormatAvailable;

        let selected_format = fmt.into_raw();
        unsafe { IsClipboardFormatAvailable(selected_format) != 0 }
    }

    /**
        Get the handle to the clipboard data and copy it's data into a new value of type `D`.
        This function is very unsafe because it assumes that the handle points to the correct type.

        The clipboard must be open when calling this function.

        If no data is found with the selected clipboard format, `None` is returned.
    */
    pub unsafe fn data<D: Copy>(fmt: ClipboardFormat) -> Option<D> {
        use winapi::um::winuser::GetClipboardData;
        use winapi::um::winbase::{GlobalLock, GlobalUnlock};
        use std::{ptr, mem};

        let fmt = fmt.into_raw();
        let handle = GetClipboardData(fmt);
        if handle.is_null() {
            return None;
        }

        let mut data = mem::zeroed();
        ptr::copy_nonoverlapping(GlobalLock(handle) as *const D, &mut data, 1);
        GlobalUnlock(handle);

        Some(data)
    }

    /**
        Gets the data handle of the clipboard, lock the memory and return it in a `ClipboardData` wrapper.
        The returned data is read-only. The application should copy the data and release the handle as soon as possible.

        The clipboard must be open when calling this function.

        If no data is found with the selected clipboard format, `None` is returned.
    */
    pub unsafe fn data_handle(fmt: ClipboardFormat) -> Option<ClipboardData> {
        use winapi::um::winuser::GetClipboardData;
        use winapi::um::winbase::GlobalLock;

        let fmt = fmt.into_raw();
        let handle = GetClipboardData(fmt);
        match handle.is_null() {
            true => None,
            false => Some(ClipboardData(GlobalLock(handle)))
        }
    }

    /**
        A window can place more than one clipboard object on the clipboard, each representing the same information in a different clipboard format.
        Retrieves the number of different data formats currently on the clipboard.
    */
    pub fn count_clipboard_formats() -> u32 {
        use winapi::um::winuser::CountClipboardFormats;
        unsafe { CountClipboardFormats() as u32 }
    }

    /**
        Empty the clipboard data.
        This is a low-level function and `open` must have been called first.
        To only clear the clipboard data use `clear`
    */
    pub fn empty() {
        use winapi::um::winuser::EmptyClipboard;
        unsafe { EmptyClipboard(); }
    }

    /**
        Close the clipboard after it was opened with the `open` function.
    */
    pub fn close() {
        use winapi::um::winuser::CloseClipboard;
        unsafe { CloseClipboard(); }
    }

    /**
        Return the handle of the window that owns the clipboard
    */
    pub fn ownder() -> ControlHandle {
        let handle = unsafe { ::winapi::um::winuser::GetClipboardOwner() };
        ControlHandle::Hwnd(handle)
    }

}

unsafe fn from_wide_ptr(ptr: *const u16) -> Option<String> {
    use std::slice::from_raw_parts;
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt;

    let mut length: isize = 0;
    while *&*ptr.offset(length) != 0 {
        length += 1;
    }

    let array: &[u16] = from_raw_parts(ptr, length as usize);

    OsString::from_wide(&array)
        .into_string()
        .ok()
}

unsafe fn from_ptr(ptr: *const u8) -> Option<String> {
    use std::slice::from_raw_parts;
    use std::str;

    let mut length: isize = 0;
    while *&*ptr.offset(length) != 0 {
        length += 1;
    }

    let array: &[u8] = from_raw_parts(ptr, length as usize);

    str::from_utf8(array)
        .map(|s| s.into())
        .ok()
}
<|endoftext|>


<commit_before>use super::Value;
use crate::map::Map;
use crate::number::Number;
use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::iter::FromIterator;

#[cfg(feature = "arbitrary_precision")]
use serde::serde_if_integer128;

macro_rules! from_integer {
    ($($ty:ident)*) => {
        $(
            impl From<$ty> for Value {
                fn from(n: $ty) -> Self {
                    Value::Number(n.into())
                }
            }
        )*
    };
}

from_integer! {
    i8 i16 i32 i64 isize
    u8 u16 u32 u64 usize
}

#[cfg(feature = "arbitrary_precision")]
serde_if_integer128! {
    from_integer! {
        i128 u128
    }
}

impl From<f32> for Value {
    /// Convert 32-bit floating point number to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let f: f32 = 13.37;
    /// let x: Value = f.into();
    /// ```
    fn from(f: f32) -> Self {
        From::from(f as f64)
    }
}

impl From<f64> for Value {
    /// Convert 64-bit floating point number to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let f: f64 = 13.37;
    /// let x: Value = f.into();
    /// ```
    fn from(f: f64) -> Self {
        Number::from_f64(f).map_or(Value::Null, Value::Number)
    }
}

impl From<bool> for Value {
    /// Convert boolean to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let b = false;
    /// let x: Value = b.into();
    /// ```
    fn from(f: bool) -> Self {
        Value::Bool(f)
    }
}

impl From<String> for Value {
    /// Convert `String` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let s: String = "lorem".to_string();
    /// let x: Value = s.into();
    /// ```
    fn from(f: String) -> Self {
        Value::String(f)
    }
}

impl<'a> From<&'a str> for Value {
    /// Convert string slice to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let s: &str = "lorem";
    /// let x: Value = s.into();
    /// ```
    fn from(f: &str) -> Self {
        Value::String(f.to_string())
    }
}

impl<'a> From<Cow<'a, str>> for Value {
    /// Convert copy-on-write string to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    /// use std::borrow::Cow;
    ///
    /// let s: Cow<str> = Cow::Borrowed("lorem");
    /// let x: Value = s.into();
    /// ```
    ///
    /// ```
    /// use serde_json::Value;
    /// use std::borrow::Cow;
    ///
    /// let s: Cow<str> = Cow::Owned("lorem".to_string());
    /// let x: Value = s.into();
    /// ```
    fn from(f: Cow<'a, str>) -> Self {
        Value::String(f.into_owned())
    }
}

impl From<Number> for Value {
    /// Convert `Number` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::{Number, Value};
    ///
    /// let n = Number::from(7);
    /// let x: Value = n.into();
    /// ```
    fn from(f: Number) -> Self {
        Value::Number(f)
    }
}

impl From<Map<String, Value>> for Value {
    /// Convert map (with string keys) to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::{Map, Value};
    ///
    /// let mut m = Map::new();
    /// m.insert("Lorem".to_string(), "ipsum".into());
    /// let x: Value = m.into();
    /// ```
    fn from(f: Map<String, Value>) -> Self {
        Value::Object(f)
    }
}

impl<T: Into<Value>> From<Vec<T>> for Value {
    /// Convert a `Vec` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v = vec!["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into();
    /// ```
    fn from(f: Vec<T>) -> Self {
        Value::Array(f.into_iter().map(Into::into).collect())
    }
}

impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
    /// Convert a slice to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: &[&str] = &["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into();
    /// ```
    fn from(f: &'a [T]) -> Self {
        Value::Array(f.iter().cloned().map(Into::into).collect())
    }
}

impl<T: Into<Value>> FromIterator<T> for Value {
    /// Convert an iteratable type to a `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v = std::iter::repeat(42).take(5);
    /// let x: Value = v.collect();
    /// ```
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: Vec<_> = vec!["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into_iter().collect();
    /// ```
    ///
    /// ```
    /// use std::iter::FromIterator;
    /// use serde_json::Value;
    ///
    /// let x: Value = Value::from_iter(vec!["lorem", "ipsum", "dolor"]);
    /// ```
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        Value::Array(iter.into_iter().map(Into::into).collect())
    }
}

impl<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Value {
    /// Convert an iteratable type to a `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: Vec<_> = vec![("lorem", 40), ("ipsum", 2)];
    /// let x: Value = v.into_iter().collect();
    /// ```
    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
        Value::Object(
            iter.into_iter()
                .map(|(k, v)| (k.into(), v.into()))
                .collect(),
        )
    }
}

impl From<()> for Value {
    /// Convert `()` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let u = ();
    /// let x: Value = u.into();
    /// ```
    fn from((): ()) -> Self {
        Value::Null
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value>,
{
    fn from(opt: Option<T>) -> Self {
        match opt {
            None => Self::Null,
            Some(value) => Into::into(value),
        }
    }
}
<commit_msg>don't use experimental (in 1.36) Self:: access for enums<commit_after>use super::Value;
use crate::map::Map;
use crate::number::Number;
use alloc::borrow::Cow;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::iter::FromIterator;

#[cfg(feature = "arbitrary_precision")]
use serde::serde_if_integer128;

macro_rules! from_integer {
    ($($ty:ident)*) => {
        $(
            impl From<$ty> for Value {
                fn from(n: $ty) -> Self {
                    Value::Number(n.into())
                }
            }
        )*
    };
}

from_integer! {
    i8 i16 i32 i64 isize
    u8 u16 u32 u64 usize
}

#[cfg(feature = "arbitrary_precision")]
serde_if_integer128! {
    from_integer! {
        i128 u128
    }
}

impl From<f32> for Value {
    /// Convert 32-bit floating point number to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let f: f32 = 13.37;
    /// let x: Value = f.into();
    /// ```
    fn from(f: f32) -> Self {
        From::from(f as f64)
    }
}

impl From<f64> for Value {
    /// Convert 64-bit floating point number to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let f: f64 = 13.37;
    /// let x: Value = f.into();
    /// ```
    fn from(f: f64) -> Self {
        Number::from_f64(f).map_or(Value::Null, Value::Number)
    }
}

impl From<bool> for Value {
    /// Convert boolean to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let b = false;
    /// let x: Value = b.into();
    /// ```
    fn from(f: bool) -> Self {
        Value::Bool(f)
    }
}

impl From<String> for Value {
    /// Convert `String` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let s: String = "lorem".to_string();
    /// let x: Value = s.into();
    /// ```
    fn from(f: String) -> Self {
        Value::String(f)
    }
}

impl<'a> From<&'a str> for Value {
    /// Convert string slice to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let s: &str = "lorem";
    /// let x: Value = s.into();
    /// ```
    fn from(f: &str) -> Self {
        Value::String(f.to_string())
    }
}

impl<'a> From<Cow<'a, str>> for Value {
    /// Convert copy-on-write string to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    /// use std::borrow::Cow;
    ///
    /// let s: Cow<str> = Cow::Borrowed("lorem");
    /// let x: Value = s.into();
    /// ```
    ///
    /// ```
    /// use serde_json::Value;
    /// use std::borrow::Cow;
    ///
    /// let s: Cow<str> = Cow::Owned("lorem".to_string());
    /// let x: Value = s.into();
    /// ```
    fn from(f: Cow<'a, str>) -> Self {
        Value::String(f.into_owned())
    }
}

impl From<Number> for Value {
    /// Convert `Number` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::{Number, Value};
    ///
    /// let n = Number::from(7);
    /// let x: Value = n.into();
    /// ```
    fn from(f: Number) -> Self {
        Value::Number(f)
    }
}

impl From<Map<String, Value>> for Value {
    /// Convert map (with string keys) to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::{Map, Value};
    ///
    /// let mut m = Map::new();
    /// m.insert("Lorem".to_string(), "ipsum".into());
    /// let x: Value = m.into();
    /// ```
    fn from(f: Map<String, Value>) -> Self {
        Value::Object(f)
    }
}

impl<T: Into<Value>> From<Vec<T>> for Value {
    /// Convert a `Vec` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v = vec!["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into();
    /// ```
    fn from(f: Vec<T>) -> Self {
        Value::Array(f.into_iter().map(Into::into).collect())
    }
}

impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
    /// Convert a slice to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: &[&str] = &["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into();
    /// ```
    fn from(f: &'a [T]) -> Self {
        Value::Array(f.iter().cloned().map(Into::into).collect())
    }
}

impl<T: Into<Value>> FromIterator<T> for Value {
    /// Convert an iteratable type to a `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v = std::iter::repeat(42).take(5);
    /// let x: Value = v.collect();
    /// ```
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: Vec<_> = vec!["lorem", "ipsum", "dolor"];
    /// let x: Value = v.into_iter().collect();
    /// ```
    ///
    /// ```
    /// use std::iter::FromIterator;
    /// use serde_json::Value;
    ///
    /// let x: Value = Value::from_iter(vec!["lorem", "ipsum", "dolor"]);
    /// ```
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        Value::Array(iter.into_iter().map(Into::into).collect())
    }
}

impl<K: Into<String>, V: Into<Value>> FromIterator<(K, V)> for Value {
    /// Convert an iteratable type to a `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let v: Vec<_> = vec![("lorem", 40), ("ipsum", 2)];
    /// let x: Value = v.into_iter().collect();
    /// ```
    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
        Value::Object(
            iter.into_iter()
                .map(|(k, v)| (k.into(), v.into()))
                .collect(),
        )
    }
}

impl From<()> for Value {
    /// Convert `()` to `Value`
    ///
    /// # Examples
    ///
    /// ```
    /// use serde_json::Value;
    ///
    /// let u = ();
    /// let x: Value = u.into();
    /// ```
    fn from((): ()) -> Self {
        Value::Null
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value>,
{
    fn from(opt: Option<T>) -> Self {
        match opt {
            None => Value::Null,
            Some(value) => Into::into(value),
        }
    }
}
<|endoftext|>


<commit_before>//! Learning agents module.
use domains::Transition;
use policies::Policy;
use Handler;

pub mod memory;

pub mod control;
pub mod prediction;

pub trait Controller<S, A>: Handler<Transition<S, A>> {
    /// Sample the target policy for a given state `s`.
    fn pi(&mut self, s: &S) -> A;

    /// Sample the behaviour policy for a given state `s`.
    fn mu(&mut self, s: &S) -> A;

    /// Sample a given policy against some state `s` for this agent.
    fn evaluate_policy<T: Policy>(&self, p: &mut T, s: &S) -> A;
}

pub trait Predictor<S: Sized>: Handler<Transition<S, ()>> {
    fn evaluate(&mut self, s: &S) -> f64;
}

pub trait TDPredictor<S: Sized>: Predictor<S> {
    fn handle_td_error(&mut self, sample: &Transition<S, ()>, td_error: f64);
    fn compute_td_error(&self, sample: &Transition<S, ()>) -> f64;
}

// TODO
// Proximal gradient-descent methods:
// https://arxiv.org/pdf/1210.4893.pdf
// https://arxiv.org/pdf/1405.6757.pdf

// TODO
// Hamid Maei Thesis (reference)
// https://era.library.ualberta.ca/files/8s45q967t/Hamid_Maei_PhDThesis.pdf
<commit_msg>feat(agents): remove `Handler` requirement in agent traits<commit_after>//! Learning agents module.
use domains::Transition;
use policies::Policy;

pub mod memory;

pub mod control;
pub mod prediction;

pub trait Controller<S, A> {
    /// Sample the target policy for a given state `s`.
    fn pi(&mut self, s: &S) -> A;

    /// Sample the behaviour policy for a given state `s`.
    fn mu(&mut self, s: &S) -> A;

    /// Sample a given policy against some state `s` for this agent.
    fn evaluate_policy<T: Policy>(&self, p: &mut T, s: &S) -> A;
}

pub trait Predictor<S: ?Sized> {
    fn evaluate(&mut self, s: &S) -> f64;
}

pub trait TDPredictor<S: Sized>: Predictor<S> {
    fn handle_td_error(&mut self, sample: &Transition<S, ()>, td_error: f64);
    fn compute_td_error(&self, sample: &Transition<S, ()>) -> f64;
}

// TODO
// Proximal gradient-descent methods:
// https://arxiv.org/pdf/1210.4893.pdf
// https://arxiv.org/pdf/1405.6757.pdf

// TODO
// Hamid Maei Thesis (reference)
// https://era.library.ualberta.ca/files/8s45q967t/Hamid_Maei_PhDThesis.pdf
<|endoftext|>


<commit_before>extern crate libc;

use self::libc::{c_char, c_uchar};

/// Sends transaction message to Sovrin network.
///
/// Adds issuer information to passed transaction json, signs it with issuer sign key
/// and sends signed transaction message to Sovring network.
///
/// #Params
/// client_id: id of sovrin client instance.
/// command_id: command id to map of callback to user context.
/// issuer: Id of Identity stored in secured Wallet.
/// tx_json: Transaction data json.
/// cb: Callback that takes command result as parameter.
///
/// #Returns
/// Transaction result as json.
///
/// #Errors
/// No method specific errors.
/// See `SovrinError` docs for common errors description.
#[no_mangle]
pub  extern fn sovrin_send_tx(client_id: i32, command_id: i32,
                              issuer: *const c_char, tx_json: *const c_char,
                              cb: extern fn(xcommand_id: i32, err: i32,
                                            tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates NYM transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_nym_tx(client_id: i32, command_id: i32,
                                 issuer: *const c_char,
                                 dest: *const c_char, verkey: *const c_char, xref: *const c_char,
                                 data: *const c_char, role: *const c_char,
                                 cb: extern fn(xcommand_id: i32, err: i32,
                                               tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates ATTRIB transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_attrib_tx(client_id: i32, command_id: i32,
                                    issuer: *const c_char, dest: *const c_char,
                                    hash: *const c_char, raw: *const c_char, enc: *const c_char,
                                    cb: extern fn(xcommand_id: i32, err: i32,
                                                  tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_ATTRIB transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_get_attrib_tx(client_id: i32, command_id: i32,
                                        issuer: *const c_char, dest: *const c_char,
                                        data: *const c_char,
                                        cb: extern fn(xcommand_id: i32, err: i32,
                                                      tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_NYM transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_get_nym_tx(client_id: i32, command_id: i32,
                                     issuer: *const c_char, dest: *const c_char,
                                     cb: extern fn(xcommand_id: i32, err: i32,
                                                   tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates SCHEMA transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_schema_tx(client_id: i32, command_id: i32,
                                    issuer: *const c_char, data: *const c_char,
                                    cb: extern fn(xcommand_id: i32, err: i32,
                                                  tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_SCHEMA transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_get_schema_tx(client_id: i32, command_id: i32,
                                        issuer: *const c_char, data: *const c_char,
                                        cb: extern fn(xcommand_id: i32, err: i32,
                                                      tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates ISSUER_KEY transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_issuer_key_tx(client_id: i32, command_id: i32,
                                        issuer: *const c_char, xref: *const c_char,
                                        data: *const c_char,
                                        cb: extern fn(xcommand_id: i32, err: i32,
                                                      tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_ISSUER_KEY transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_get_issuer_key_tx(client_id: i32, command_id: i32,
                                            issuer: *const c_char, xref: *const c_char,
                                            cb: extern fn(xcommand_id: i32, err: i32,
                                                          tx_result_json: *const c_char)) {
    unimplemented!();
}

// Creates NODE transaction message and sends it to Sovrin network.
#[no_mangle]
pub extern fn sovrin_send_node_tx(client_id: i32, command_id: i32,
                                  issuer: *const c_char, dest: *const c_char, data: *const c_char,
                                  cb: extern fn(xcommand_id: i32, err: i32,
                                                tx_result_json: *const c_char)) {
    unimplemented!();
}<commit_msg>changes for api<commit_after>extern crate libc;

use self::libc::{c_char, c_uchar};

#[no_mangle]
pub extern fn ledger_check_verkey(client_handle: i32, command_handle: i32,
                                   did: *const c_char, verkey: *const c_char,
                                   cb: extern fn(xcommand_handle: i32, err: i32,
                                                 xverkey: *const c_char)) {
    unimplemented!();
}

#[no_mangle]
pub extern fn ledger_read_ddo(client_handle: i32, command_handle: i32,
                               submitter_did: *const c_char, target_did: *const c_char,
                               cb: extern fn(xcommand_handle: i32, err: i32,
                                             txn_result_json: *const c_char)) {
    unimplemented!();
}

/// Sends transaction message to Ledger network.
///
/// Adds issuer information to passed transaction json, signs it with issuer sign key
/// and sends signed transaction message to Ledger network.
///
/// #Params
/// client_handle: id of Ledger client instance.
/// command_handle: command id to map of callback to user context.
/// submitter_did: Id of Identity stored in secured Wallet.
/// txn_json: Transaction data json.
/// cb: Callback that takes command result as parameter.
///
/// #Returns
/// Transaction result as json.
///
/// #Errors
/// No method specific errors.
/// See `LedgerError` docs for common errors description.
#[no_mangle]
pub extern fn ledger_write_txn(client_handle: i32, command_handle: i32,
                                submitter_did: *const c_char, txn_json: *const c_char,
                                cb: extern fn(xcommand_handle: i32, err: i32,
                                              txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates NYM transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_write_nym_txn(client_handle: i32, command_handle: i32,
                                   submitter_did: *const c_char,
                                   target_did: *const c_char,
                                   verkey: *const c_char, xref: *const c_char,
                                   data: *const c_char, role: *const c_char,
                                   cb: extern fn(xcommand_handle: i32, err: i32,
                                                 txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates ATTRIB transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_write_attrib_txn(client_handle: i32, command_handle: i32,
                                      submitter_did: *const c_char, target_did: *const c_char,
                                      hash: *const c_char, raw: *const c_char, enc: *const c_char,
                                      cb: extern fn(xcommand_handle: i32, err: i32,
                                                    txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_ATTRIB transaction message and sends it to Ledger network.
pub extern fn ledger_read_attrib(client_handle: i32, command_handle: i32,
                                 submitter_did: *const c_char, target_did: *const c_char,
                                 data: *const c_char,
                                 cb: extern fn(xcommand_handle: i32, err: i32,
                                               txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_NYM transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_read_nym(client_handle: i32, command_handle: i32,
                              submitter_did: *const c_char, target_did: *const c_char,
                              cb: extern fn(xcommand_handle: i32, err: i32,
                                            txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates SCHEMA transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_write_schema_txn(client_handle: i32, command_handle: i32,
                                      submitter_did: *const c_char, data: *const c_char,
                                      cb: extern fn(xcommand_handle: i32, err: i32,
                                                    txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_SCHEMA transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_read_schema(client_handle: i32, command_handle: i32,
                                 submitter_did: *const c_char, data: *const c_char,
                                 cb: extern fn(xcommand_handle: i32, err: i32,
                                               txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates ISSUER_KEY transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_write_issuer_key_txn(client_handle: i32, command_handle: i32,
                                          submitter_did: *const c_char, xref: *const c_char,
                                          data: *const c_char,
                                          cb: extern fn(xcommand_handle: i32, err: i32,
                                                        txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates GET_ISSUER_KEY transaction message and sends it to Ledger network.
pub extern fn ledger_read_issuer_key(client_handle: i32, command_handle: i32,
                                     submitter_did: *const c_char, xref: *const c_char,
                                     cb: extern fn(xcommand_handle: i32, err: i32,
                                                   txn_result_json: *const c_char)) {
    unimplemented!();
}

// Creates NODE transaction message and sends it to Ledger network.
#[no_mangle]
pub extern fn ledger_write_node_txn(client_handle: i32, command_handle: i32,
                                    submitter_did: *const c_char, target_did: *const c_char,
                                    data: *const c_char,
                                    cb: extern fn(xcommand_handle: i32, err: i32,
                                                  txn_result_json: *const c_char)) {
    unimplemented!();
}<|endoftext|>


<commit_before>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod logger;
mod rustc_wrapper;

use std::{env, fs, path::Path, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{cli::flags, config::Config, from_json, lsp_ext::supports_utf8, Result};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if std::env::var("RA_RUSTC_WRAPPER").is_ok() {
        let mut args = std::env::args_os();
        let _me = args.next().unwrap();
        let rustc = args.next().unwrap();
        let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) {
            Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102),
            Err(err) => {
                eprintln!("{}", err);
                101
            }
        };
        process::exit(code);
    }

    if let Err(err) = try_main() {
        tracing::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let flags = flags::RustAnalyzer::from_env()?;

    #[cfg(debug_assertions)]
    if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    let mut log_file = flags.log_file.as_deref();

    let env_log_file = env::var("RA_LOG_FILE").ok();
    if let Some(env_log_file) = env_log_file.as_deref() {
        log_file = Some(Path::new(env_log_file));
    }

    setup_logging(log_file)?;
    let verbosity = flags.verbosity();

    match flags.subcommand {
        flags::RustAnalyzerCmd::LspServer(cmd) => {
            if cmd.print_config_schema {
                println!("{:#}", Config::json_schema());
                return Ok(());
            }
            if cmd.version {
                println!("rust-analyzer {}", env!("REV"));
                return Ok(());
            }
            if cmd.help {
                println!("{}", flags::RustAnalyzer::HELP);
                return Ok(());
            }
            run_server()?
        }
        flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => proc_macro_srv::cli::run()?,
        flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?,
        flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
    }
    Ok(())
}

fn setup_logging(log_file: Option<&Path>) -> Result<()> {
    if cfg!(windows) {
        // This is required so that windows finds our pdb that is placed right beside the exe.
        // By default it doesn't look at the folder the exe resides in, only in the current working
        // directory which we set to the project workspace.
        // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/general-environment-variables
        // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize
        if let Ok(path) = env::current_exe() {
            if let Some(path) = path.parent() {
                env::set_var("_NT_SYMBOL_PATH", path);
            }
        }
    }
    if env::var("RUST_BACKTRACE").is_err() {
        env::set_var("RUST_BACKTRACE", "short");
    }

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    // deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually useful
    // information in there for debugging
    logger::Logger::new(log_file, filter.as_deref().or(Some("error"))).install()?;

    profile::init();

    Ok(())
}

fn run_server() -> Result<()> {
    tracing::info!("server version {} will start", env!("REV"));

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    tracing::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let root_path = match initialize_params
        .root_uri
        .and_then(|it| it.to_file_path().ok())
        .and_then(|it| AbsPathBuf::try_from(it).ok())
    {
        Some(it) => it,
        None => {
            let cwd = env::current_dir()?;
            AbsPathBuf::assert(cwd)
        }
    };

    let mut config = Config::new(root_path, initialize_params.capabilities);
    if let Some(json) = initialize_params.initialization_options {
        if let Err(e) = config.update(json) {
            use lsp_types::{
                notification::{Notification, ShowMessage},
                MessageType, ShowMessageParams,
            };
            let not = lsp_server::Notification::new(
                ShowMessage::METHOD.to_string(),
                ShowMessageParams { typ: MessageType::WARNING, message: e.to_string() },
            );
            connection.sender.send(lsp_server::Message::Notification(not)).unwrap();
        }
    }

    let server_capabilities = rust_analyzer::server_capabilities(&config);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
        offset_encoding: if supports_utf8(config.caps()) {
            Some("utf-8".to_string())
        } else {
            None
        },
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    if config.linked_projects().is_empty() && config.detached_files().is_empty() {
        let workspace_roots = initialize_params
            .workspace_folders
            .map(|workspaces| {
                workspaces
                    .into_iter()
                    .filter_map(|it| it.uri.to_file_path().ok())
                    .filter_map(|it| AbsPathBuf::try_from(it).ok())
                    .collect::<Vec<_>>()
            })
            .filter(|workspaces| !workspaces.is_empty())
            .unwrap_or_else(|| vec![config.root_path().clone()]);

        let discovered = ProjectManifest::discover_all(&workspace_roots);
        tracing::info!("discovered projects: {:?}", discovered);
        if discovered.is_empty() {
            tracing::error!("failed to find any projects in {:?}", workspace_roots);
        }
        config.discovered_projects = Some(discovered);
    }

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    tracing::info!("server did shut down");
    Ok(())
}
<commit_msg>Auto merge of #12058 - jonas-schievink:one-thread-with-extra-stack-please, r=jonas-schievink<commit_after>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod logger;
mod rustc_wrapper;

use std::{env, fs, path::Path, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{cli::flags, config::Config, from_json, lsp_ext::supports_utf8, Result};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if std::env::var("RA_RUSTC_WRAPPER").is_ok() {
        let mut args = std::env::args_os();
        let _me = args.next().unwrap();
        let rustc = args.next().unwrap();
        let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) {
            Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102),
            Err(err) => {
                eprintln!("{}", err);
                101
            }
        };
        process::exit(code);
    }

    if let Err(err) = try_main() {
        tracing::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let flags = flags::RustAnalyzer::from_env()?;

    #[cfg(debug_assertions)]
    if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    let mut log_file = flags.log_file.as_deref();

    let env_log_file = env::var("RA_LOG_FILE").ok();
    if let Some(env_log_file) = env_log_file.as_deref() {
        log_file = Some(Path::new(env_log_file));
    }

    setup_logging(log_file)?;
    let verbosity = flags.verbosity();

    match flags.subcommand {
        flags::RustAnalyzerCmd::LspServer(cmd) => {
            if cmd.print_config_schema {
                println!("{:#}", Config::json_schema());
                return Ok(());
            }
            if cmd.version {
                println!("rust-analyzer {}", env!("REV"));
                return Ok(());
            }
            if cmd.help {
                println!("{}", flags::RustAnalyzer::HELP);
                return Ok(());
            }
            with_extra_thread("rust-analyzer server thread", run_server)?;
        }
        flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
            with_extra_thread("rust-analyzer proc-macro expander", || {
                proc_macro_srv::cli::run().map_err(Into::into)
            })?;
        }
        flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?,
        flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
        flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
    }
    Ok(())
}

fn setup_logging(log_file: Option<&Path>) -> Result<()> {
    if cfg!(windows) {
        // This is required so that windows finds our pdb that is placed right beside the exe.
        // By default it doesn't look at the folder the exe resides in, only in the current working
        // directory which we set to the project workspace.
        // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/general-environment-variables
        // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize
        if let Ok(path) = env::current_exe() {
            if let Some(path) = path.parent() {
                env::set_var("_NT_SYMBOL_PATH", path);
            }
        }
    }
    if env::var("RUST_BACKTRACE").is_err() {
        env::set_var("RUST_BACKTRACE", "short");
    }

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    // deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually useful
    // information in there for debugging
    logger::Logger::new(log_file, filter.as_deref().or(Some("error"))).install()?;

    profile::init();

    Ok(())
}

const STACK_SIZE: usize = 1024 * 1024 * 8;

/// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us
/// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack
/// space.
fn with_extra_thread(
    thread_name: impl Into<String>,
    f: impl FnOnce() -> Result<()> + Send + 'static,
) -> Result<()> {
    let handle =
        std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?;
    match handle.join() {
        Ok(res) => res,
        Err(panic) => std::panic::resume_unwind(panic),
    }
}

fn run_server() -> Result<()> {
    tracing::info!("server version {} will start", env!("REV"));

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    tracing::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let root_path = match initialize_params
        .root_uri
        .and_then(|it| it.to_file_path().ok())
        .and_then(|it| AbsPathBuf::try_from(it).ok())
    {
        Some(it) => it,
        None => {
            let cwd = env::current_dir()?;
            AbsPathBuf::assert(cwd)
        }
    };

    let mut config = Config::new(root_path, initialize_params.capabilities);
    if let Some(json) = initialize_params.initialization_options {
        if let Err(e) = config.update(json) {
            use lsp_types::{
                notification::{Notification, ShowMessage},
                MessageType, ShowMessageParams,
            };
            let not = lsp_server::Notification::new(
                ShowMessage::METHOD.to_string(),
                ShowMessageParams { typ: MessageType::WARNING, message: e.to_string() },
            );
            connection.sender.send(lsp_server::Message::Notification(not)).unwrap();
        }
    }

    let server_capabilities = rust_analyzer::server_capabilities(&config);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
        offset_encoding: if supports_utf8(config.caps()) {
            Some("utf-8".to_string())
        } else {
            None
        },
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    if config.linked_projects().is_empty() && config.detached_files().is_empty() {
        let workspace_roots = initialize_params
            .workspace_folders
            .map(|workspaces| {
                workspaces
                    .into_iter()
                    .filter_map(|it| it.uri.to_file_path().ok())
                    .filter_map(|it| AbsPathBuf::try_from(it).ok())
                    .collect::<Vec<_>>()
            })
            .filter(|workspaces| !workspaces.is_empty())
            .unwrap_or_else(|| vec![config.root_path().clone()]);

        let discovered = ProjectManifest::discover_all(&workspace_roots);
        tracing::info!("discovered projects: {:?}", discovered);
        if discovered.is_empty() {
            tracing::error!("failed to find any projects in {:?}", workspace_roots);
        }
        config.discovered_projects = Some(discovered);
    }

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    tracing::info!("server did shut down");
    Ok(())
}
<|endoftext|>


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

use std::sync::Arc;
use std::path::Path;

use hab_core::util::sys;
use hyper::client::Client as HyperClient;
use hyper::client::RequestBuilder;
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::{HttpsConnector, Openssl};
use openssl::ssl::{SslContext, SslMethod, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new(endpoint: &Url,
               product: &str,
               version: &str,
               fs_root_path: Option<&Path>)
               -> Result<Self> {
        Ok(ApiClient {
            endpoint: endpoint.clone(),
            inner: try!(new_hyper_client(Some(endpoint), fs_root_path)),
            proxy: try!(proxy_unless_domain_exempted(Some(endpoint))),
            target_scheme: endpoint.scheme().to_string(),
            user_agent_header: try!(user_agent(product, version)),
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tuneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boild down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Interally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platoform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(for_domain: Option<&Url>, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let ctx = try!(ssl_ctx(fs_root_path));
    let ssl_client = Openssl { context: Arc::new(ctx) };
    match try!(proxy_unless_domain_exempted(for_domain)) {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = try!(ProxyHttpsConnector::new(proxy, ssl_client));
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel seperated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = try!(sys::uname());
    let ua = format!("{}/{} ({}-{}; {})",
                     product,
                     version,
                     uname.machine.to_lowercase(),
                     uname.sys_name.to_lowercase(),
                     uname.release.to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_ctx(fs_root_path: Option<&Path>) -> Result<SslContext> {
    // The spirit of this implementation is directly from Hyper's default OpensslClient function:
    // https://github.com/hyperium/hyper/blob/v0.9.5/src/net.rs#L653-L661
    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ssl::set_ca(&mut ctx, fs_root_path));
    ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
    try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH"));
    Ok(ctx)
}
<commit_msg>Fix connection-closed error when passing bad headers<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;
use std::path::Path;

use hab_core::util::sys;
use hyper::client::Client as HyperClient;
use hyper::client::RequestBuilder;
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::{HttpsConnector, Openssl};
use openssl::ssl::{SslContext, SslMethod, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new(endpoint: &Url,
               product: &str,
               version: &str,
               fs_root_path: Option<&Path>)
               -> Result<Self> {
        Ok(ApiClient {
            endpoint: endpoint.clone(),
            inner: try!(new_hyper_client(Some(endpoint), fs_root_path)),
            proxy: try!(proxy_unless_domain_exempted(Some(endpoint))),
            target_scheme: endpoint.scheme().to_string(),
            user_agent_header: try!(user_agent(product, version)),
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tuneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boild down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Interally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platoform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(for_domain: Option<&Url>, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let ctx = try!(ssl_ctx(fs_root_path));
    let ssl_client = Openssl { context: Arc::new(ctx) };
    match try!(proxy_unless_domain_exempted(for_domain)) {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = try!(ProxyHttpsConnector::new(proxy, ssl_client));
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel seperated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = try!(sys::uname());
    let ua = format!("{}/{} ({}-{}; {})",
                     product.trim(),
                     version.trim(),
                     uname.machine.trim().to_lowercase(),
                     uname.sys_name.trim().to_lowercase(),
                     uname.release.trim().to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_ctx(fs_root_path: Option<&Path>) -> Result<SslContext> {
    // The spirit of this implementation is directly from Hyper's default OpensslClient function:
    // https://github.com/hyperium/hyper/blob/v0.9.5/src/net.rs#L653-L661
    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ssl::set_ca(&mut ctx, fs_root_path));
    ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
    try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH"));
    Ok(ctx)
}
<|endoftext|>