<commit_before>#![allow(missing_docs, nonstandard_style)]

use crate::ffi::{OsStr, OsString};
use crate::io::ErrorKind;
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::path::PathBuf;
use crate::ptr;
use crate::time::Duration;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod compat;

pub mod alloc;
pub mod args;
pub mod c;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fast_thread_local;
pub mod fs;
pub mod handle;
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod thread;
pub mod thread_local;
pub mod time;
cfg_if::cfg_if! {
    if #[cfg(not(target_vendor = "uwp"))] {
        pub mod stdio;
        pub mod stack_overflow;
    } else {
        pub mod stdio_uwp;
        pub mod stack_overflow_uwp;
        pub use self::stdio_uwp as stdio;
        pub use self::stack_overflow_uwp as stack_overflow;
    }
}

#[cfg(not(test))]
pub fn init() {}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as c::DWORD {
        c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied,
        c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe,
        c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_NO_DATA => return ErrorKind::BrokenPipe,
        c::ERROR_SEM_TIMEOUT
        | c::WAIT_TIMEOUT
        | c::ERROR_DRIVER_CANCEL_TIMEOUT
        | c::ERROR_OPERATION_ABORTED
        | c::ERROR_SERVICE_REQUEST_TIMEOUT
        | c::ERROR_COUNTER_TIMEOUT
        | c::ERROR_TIMEOUT
        | c::ERROR_RESOURCE_CALL_TIMED_OUT
        | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
        | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
        | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
        | c::ERROR_DS_TIMELIMIT_EXCEEDED
        | c::DNS_ERROR_RECORD_TIMED_OUT
        | c::ERROR_IPSEC_IKE_TIMED_OUT
        | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
        | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
        _ => {}
    }

    match errno {
        c::WSAEACCES => ErrorKind::PermissionDenied,
        c::WSAEADDRINUSE => ErrorKind::AddrInUse,
        c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        c::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        c::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        c::WSAECONNRESET => ErrorKind::ConnectionReset,
        c::WSAEINVAL => ErrorKind::InvalidInput,
        c::WSAENOTCONN => ErrorKind::NotConnected,
        c::WSAEWOULDBLOCK => ErrorKind::WouldBlock,
        c::WSAETIMEDOUT => ErrorKind::TimedOut,

        _ => ErrorKind::Other,
    }
}

pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
    let ptr = haystack.as_ptr();
    let mut len = haystack.len();
    let mut start = &haystack[..];

    // For performance reasons unfold the loop eight times.
    while len >= 8 {
        if start[0] == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2);
        }
        if start[1] == needle {
            return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[2] == needle {
            return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[3] == needle {
            return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[4] == needle {
            return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[5] == needle {
            return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[6] == needle {
            return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[7] == needle {
            return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
        }

        start = &start[8..];
        len -= 8;
    }

    for (i, c) in start.iter().enumerate() {
        if *c == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
        }
    }
    None
}

pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
    fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
        let mut maybe_result: Vec<u16> = s.encode_wide().collect();
        if unrolled_find_u16s(0, &maybe_result).is_some() {
            return Err(crate::io::Error::new(
                ErrorKind::InvalidInput,
                "strings passed to WinAPI cannot contain NULs",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
    inner(s.as_ref())
}

// Many Windows APIs follow a pattern of where we hand a buffer and then they
// will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
where
    F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
    F2: FnOnce(&[u16]) -> T,
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
                0 if c::GetLastError() == 0 => 0,
                0 => return Err(crate::io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]));
            }
        }
    }
}

fn os2path(s: &[u16]) -> PathBuf {
    PathBuf::from(OsString::from_wide(s))
}

#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename()
fn wide_char_to_multi_byte(
    code_page: u32,
    flags: u32,
    s: &[u16],
    no_default_char: bool,
) -> crate::io::Result<Vec<i8>> {
    unsafe {
        let mut size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            ptr::null_mut(),
            0,
            ptr::null(),
            ptr::null_mut(),
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }

        let mut buf = Vec::with_capacity(size as usize);
        buf.set_len(size as usize);

        let mut used_default_char = c::FALSE;
        size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            buf.as_mut_ptr(),
            buf.len() as i32,
            ptr::null(),
            if no_default_char { &mut used_default_char } else { ptr::null_mut() },
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }
        if no_default_char && used_default_char == c::TRUE {
            return Err(crate::io::Error::new(
                crate::io::ErrorKind::InvalidData,
                "string cannot be converted to requested code page",
            ));
        }

        buf.set_len(size as usize);

        Ok(buf)
    }
}

pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
    match unrolled_find_u16s(0, v) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v,
    }
}

pub trait IsZero {
    fn is_zero(&self) -> bool;
}

macro_rules! impl_is_zero {
    ($($t:ident)*) => ($(impl IsZero for $t {
        fn is_zero(&self) -> bool {
            *self == 0
        }
    })*)
}

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

pub fn cvt<I: IsZero>(i: I) -> crate::io::Result<I> {
    if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) }
}

pub fn dur2timeout(dur: Duration) -> c::DWORD {
    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
    // have two pieces to take care of:
    //
    // * Nanosecond precision is rounded up
    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
    //   (never time out).
    dur.as_secs()
        .checked_mul(1000)
        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
        .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 }))
        .map(|ms| if ms > <c::DWORD>::MAX as u64 { c::INFINITE } else { ms as c::DWORD })
        .unwrap_or(c::INFINITE)
}

// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
// and later, this will terminate the process immediately without running any
// in-process exception handlers.  In earlier versions of Windows, this
// sequence of instructions will be treated as an access violation,
// terminating the process but without necessarily bypassing all exception
// handlers.
//
// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
#[allow(unreachable_code)]
pub fn abort_internal() -> ! {
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    unsafe {
        llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
        crate::intrinsics::unreachable();
    }
    crate::intrinsics::abort();
}
<commit_msg>Rollup merge of #73752 - TyPR124:invalid-parameter-error, r=LukasKalbertodt<commit_after>#![allow(missing_docs, nonstandard_style)]

use crate::ffi::{OsStr, OsString};
use crate::io::ErrorKind;
use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::path::PathBuf;
use crate::ptr;
use crate::time::Duration;

pub use self::rand::hashmap_random_keys;
pub use libc::strlen;

#[macro_use]
pub mod compat;

pub mod alloc;
pub mod args;
pub mod c;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fast_thread_local;
pub mod fs;
pub mod handle;
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod thread;
pub mod thread_local;
pub mod time;
cfg_if::cfg_if! {
    if #[cfg(not(target_vendor = "uwp"))] {
        pub mod stdio;
        pub mod stack_overflow;
    } else {
        pub mod stdio_uwp;
        pub mod stack_overflow_uwp;
        pub use self::stdio_uwp as stdio;
        pub use self::stack_overflow_uwp as stack_overflow;
    }
}

#[cfg(not(test))]
pub fn init() {}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as c::DWORD {
        c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied,
        c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists,
        c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe,
        c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
        c::ERROR_NO_DATA => return ErrorKind::BrokenPipe,
        c::ERROR_INVALID_PARAMETER => return ErrorKind::InvalidInput,
        c::ERROR_SEM_TIMEOUT
        | c::WAIT_TIMEOUT
        | c::ERROR_DRIVER_CANCEL_TIMEOUT
        | c::ERROR_OPERATION_ABORTED
        | c::ERROR_SERVICE_REQUEST_TIMEOUT
        | c::ERROR_COUNTER_TIMEOUT
        | c::ERROR_TIMEOUT
        | c::ERROR_RESOURCE_CALL_TIMED_OUT
        | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT
        | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT
        | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT
        | c::ERROR_DS_TIMELIMIT_EXCEEDED
        | c::DNS_ERROR_RECORD_TIMED_OUT
        | c::ERROR_IPSEC_IKE_TIMED_OUT
        | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
        | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
        _ => {}
    }

    match errno {
        c::WSAEACCES => ErrorKind::PermissionDenied,
        c::WSAEADDRINUSE => ErrorKind::AddrInUse,
        c::WSAEADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        c::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        c::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        c::WSAECONNRESET => ErrorKind::ConnectionReset,
        c::WSAEINVAL => ErrorKind::InvalidInput,
        c::WSAENOTCONN => ErrorKind::NotConnected,
        c::WSAEWOULDBLOCK => ErrorKind::WouldBlock,
        c::WSAETIMEDOUT => ErrorKind::TimedOut,

        _ => ErrorKind::Other,
    }
}

pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
    let ptr = haystack.as_ptr();
    let mut len = haystack.len();
    let mut start = &haystack[..];

    // For performance reasons unfold the loop eight times.
    while len >= 8 {
        if start[0] == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2);
        }
        if start[1] == needle {
            return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[2] == needle {
            return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[3] == needle {
            return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[4] == needle {
            return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[5] == needle {
            return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[6] == needle {
            return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
        }
        if start[7] == needle {
            return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
        }

        start = &start[8..];
        len -= 8;
    }

    for (i, c) in start.iter().enumerate() {
        if *c == needle {
            return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
        }
    }
    None
}

pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
    fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
        let mut maybe_result: Vec<u16> = s.encode_wide().collect();
        if unrolled_find_u16s(0, &maybe_result).is_some() {
            return Err(crate::io::Error::new(
                ErrorKind::InvalidInput,
                "strings passed to WinAPI cannot contain NULs",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }
    inner(s.as_ref())
}

// Many Windows APIs follow a pattern of where we hand a buffer and then they
// will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
where
    F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
    F2: FnOnce(&[u16]) -> T,
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as c::DWORD) {
                0 if c::GetLastError() == 0 => 0,
                0 => return Err(crate::io::Error::last_os_error()),
                n => n,
            } as usize;
            if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]));
            }
        }
    }
}

fn os2path(s: &[u16]) -> PathBuf {
    PathBuf::from(OsString::from_wide(s))
}

#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename()
fn wide_char_to_multi_byte(
    code_page: u32,
    flags: u32,
    s: &[u16],
    no_default_char: bool,
) -> crate::io::Result<Vec<i8>> {
    unsafe {
        let mut size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            ptr::null_mut(),
            0,
            ptr::null(),
            ptr::null_mut(),
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }

        let mut buf = Vec::with_capacity(size as usize);
        buf.set_len(size as usize);

        let mut used_default_char = c::FALSE;
        size = c::WideCharToMultiByte(
            code_page,
            flags,
            s.as_ptr(),
            s.len() as i32,
            buf.as_mut_ptr(),
            buf.len() as i32,
            ptr::null(),
            if no_default_char { &mut used_default_char } else { ptr::null_mut() },
        );
        if size == 0 {
            return Err(crate::io::Error::last_os_error());
        }
        if no_default_char && used_default_char == c::TRUE {
            return Err(crate::io::Error::new(
                crate::io::ErrorKind::InvalidData,
                "string cannot be converted to requested code page",
            ));
        }

        buf.set_len(size as usize);

        Ok(buf)
    }
}

pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] {
    match unrolled_find_u16s(0, v) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v,
    }
}

pub trait IsZero {
    fn is_zero(&self) -> bool;
}

macro_rules! impl_is_zero {
    ($($t:ident)*) => ($(impl IsZero for $t {
        fn is_zero(&self) -> bool {
            *self == 0
        }
    })*)
}

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

pub fn cvt<I: IsZero>(i: I) -> crate::io::Result<I> {
    if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) }
}

pub fn dur2timeout(dur: Duration) -> c::DWORD {
    // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
    // timeouts in windows APIs are typically u32 milliseconds. To translate, we
    // have two pieces to take care of:
    //
    // * Nanosecond precision is rounded up
    // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
    //   (never time out).
    dur.as_secs()
        .checked_mul(1000)
        .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
        .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 }))
        .map(|ms| if ms > <c::DWORD>::MAX as u64 { c::INFINITE } else { ms as c::DWORD })
        .unwrap_or(c::INFINITE)
}

// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
// and later, this will terminate the process immediately without running any
// in-process exception handlers.  In earlier versions of Windows, this
// sequence of instructions will be treated as an access violation,
// terminating the process but without necessarily bypassing all exception
// handlers.
//
// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail
#[allow(unreachable_code)]
pub fn abort_internal() -> ! {
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    unsafe {
        llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
        crate::intrinsics::unreachable();
    }
    crate::intrinsics::abort();
}
<|endoftext|>


<commit_before>#![unstable(issue = "0", feature = "windows_net")]

use crate::cmp;
use crate::io::{self, Read, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{SocketAddr, Shutdown};
use crate::ptr;
use crate::sync::Once;
use crate::sys::c;
use crate::sys;
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
use crate::sys_common::net;
use crate::time::Duration;

use libc::{c_int, c_void, c_ulong, c_long};

pub type wrlen_t = i32;

pub mod netc {
    pub use crate::sys::c::*;
    pub use crate::sys::c::SOCKADDR as sockaddr;
    pub use crate::sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage;
    pub use crate::sys::c::ADDRINFOA as addrinfo;
    pub use crate::sys::c::ADDRESS_FAMILY as sa_family_t;
}

pub struct Socket(c::SOCKET);

/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
pub fn init() {
    static START: Once = Once::new();

    START.call_once(|| unsafe {
        let mut data: c::WSADATA = mem::zeroed();
        let ret = c::WSAStartup(0x202, // version 2.2
                                &mut data);
        assert_eq!(ret, 0);

        let _ = sys_common::at_exit(|| { c::WSACleanup(); });
    });
}

/// Returns the last error from the Windows socket interface.
fn last_error() -> io::Error {
    io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
/// and if so, returns the last error from the Windows socket interface. This
/// function must be called before another call to the socket API is made.
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
    if t.is_minus_one() {
        Err(last_error())
    } else {
        Ok(t)
    }
}

/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
pub fn cvt_gai(err: c_int) -> io::Result<()> {
    if err == 0 {
        Ok(())
    } else {
        Err(last_error())
    }
}

/// Just to provide the same interface as sys/unix/net.rs
pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
    where T: IsMinusOne,
          F: FnMut() -> T
{
    cvt(f())
}

impl Socket {
    pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
        let fam = match *addr {
            SocketAddr::V4(..) => c::AF_INET,
            SocketAddr::V6(..) => c::AF_INET6,
        };
        let socket = unsafe {
            match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0,
                                c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) {
                c::INVALID_SOCKET => {
                    match c::WSAGetLastError() {
                        c::WSAEPROTOTYPE => {
                            match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0,
                                                c::WSA_FLAG_OVERLAPPED) {
                                c::INVALID_SOCKET => Err(last_error()),
                                n => {
                                    let s = Socket(n);
                                    s.set_no_inherit()?;
                                    Ok(s)
                                },
                            }
                        },
                        n => Err(io::Error::from_raw_os_error(n)),
                    }
                },
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
        self.set_nonblocking(true)?;
        let r = unsafe {
            let (addrp, len) = addr.into_inner();
            cvt(c::connect(self.0, addrp, len))
        };
        self.set_nonblocking(false)?;

        match r {
            Ok(_) => return Ok(()),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
            Err(e) => return Err(e),
        }

        if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
            return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                      "cannot set a 0 duration timeout"));
        }

        let mut timeout = c::timeval {
            tv_sec: timeout.as_secs() as c_long,
            tv_usec: (timeout.subsec_nanos() / 1000) as c_long,
        };
        if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
            timeout.tv_usec = 1;
        }

        let fds = unsafe {
            let mut fds = mem::zeroed::<c::fd_set>();
            fds.fd_count = 1;
            fds.fd_array[0] = self.0;
            fds
        };

        let mut writefds = fds;
        let mut errorfds = fds;

        let n = unsafe {
            cvt(c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout))?
        };

        match n {
            0 => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")),
            _ => {
                if writefds.fd_count != 1 {
                    if let Some(e) = self.take_error()? {
                        return Err(e);
                    }
                }
                Ok(())
            }
        }
    }

    pub fn accept(&self, storage: *mut c::SOCKADDR,
                  len: *mut c_int) -> io::Result<Socket> {
        let socket = unsafe {
            match c::accept(self.0, storage, len) {
                c::INVALID_SOCKET => Err(last_error()),
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    pub fn duplicate(&self) -> io::Result<Socket> {
        let socket = unsafe {
            let mut info: c::WSAPROTOCOL_INFO = mem::zeroed();
            cvt(c::WSADuplicateSocketW(self.0,
                                            c::GetCurrentProcessId(),
                                            &mut info))?;

            match c::WSASocketW(info.iAddressFamily,
                                info.iSocketType,
                                info.iProtocol,
                                &mut info, 0,
                                c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) {
                c::INVALID_SOCKET => {
                    match c::WSAGetLastError() {
                        c::WSAEPROTOTYPE => {
                            match c::WSASocketW(info.iAddressFamily,
                                                info.iSocketType,
                                                info.iProtocol,
                                                &mut info, 0,
                                                c::WSA_FLAG_OVERLAPPED) {
                                c::INVALID_SOCKET => Err(last_error()),
                                n => {
                                    let s = Socket(n);
                                    s.set_no_inherit()?;
                                    Ok(s)
                                },
                            }
                        },
                        n => Err(io::Error::from_raw_os_error(n)),
                    }
                },
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        let len = cmp::min(buf.len(), i32::max_value() as usize) as i32;
        unsafe {
            match c::recv(self.0, buf.as_mut_ptr() as *mut c_void, len, flags) {
                -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0),
                -1 => Err(last_error()),
                n => Ok(n as usize)
            }
        }
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        self.recv_with_flags(buf, 0)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
        let mut nread = 0;
        let mut flags = 0;
        unsafe {
            let ret = c::WSARecv(
                self.0,
                bufs.as_mut_ptr() as *mut c::WSABUF,
                len,
                &mut nread,
                &mut flags,
                ptr::null_mut(),
                ptr::null_mut(),
            );
            match ret {
                0 => Ok(nread as usize),
                _ if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0),
                _ => Err(last_error()),
            }
        }
    }

    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
        self.recv_with_flags(buf, c::MSG_PEEK)
    }

    fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int)
                            -> io::Result<(usize, SocketAddr)> {
        let mut storage: c::SOCKADDR_STORAGE_LH = unsafe { mem::zeroed() };
        let mut addrlen = mem::size_of_val(&storage) as c::socklen_t;
        let len = cmp::min(buf.len(), <wrlen_t>::max_value() as usize) as wrlen_t;

        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        unsafe {
            match c::recvfrom(self.0,
                              buf.as_mut_ptr() as *mut c_void,
                              len,
                              flags,
                              &mut storage as *mut _ as *mut _,
                              &mut addrlen) {
                -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => {
                    Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?))
                },
                -1 => Err(last_error()),
                n => Ok((n as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)),
            }
        }
    }

    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        self.recv_from_with_flags(buf, 0)
    }

    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        self.recv_from_with_flags(buf, c::MSG_PEEK)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
        let mut nwritten = 0;
        unsafe {
            cvt(c::WSASend(
                self.0,
                bufs.as_ptr() as *const c::WSABUF as *mut c::WSABUF,
                len,
                &mut nwritten,
                0,
                ptr::null_mut(),
                ptr::null_mut(),
            ))?;
        }
        Ok(nwritten as usize)
    }

    pub fn set_timeout(&self, dur: Option<Duration>,
                       kind: c_int) -> io::Result<()> {
        let timeout = match dur {
            Some(dur) => {
                let timeout = sys::dur2timeout(dur);
                if timeout == 0 {
                    return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                              "cannot set a 0 duration timeout"));
                }
                timeout
            }
            None => 0
        };
        net::setsockopt(self, c::SOL_SOCKET, kind, timeout)
    }

    pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
        let raw: c::DWORD = net::getsockopt(self, c::SOL_SOCKET, kind)?;
        if raw == 0 {
            Ok(None)
        } else {
            let secs = raw / 1000;
            let nsec = (raw % 1000) * 1000000;
            Ok(Some(Duration::new(secs as u64, nsec as u32)))
        }
    }

    #[cfg(not(target_vendor = "uwp"))]
    fn set_no_inherit(&self) -> io::Result<()> {
        sys::cvt(unsafe {
            c::SetHandleInformation(self.0 as c::HANDLE,
                                    c::HANDLE_FLAG_INHERIT, 0)
        }).map(|_| ())
    }

    #[cfg(target_vendor = "uwp")]
    fn set_no_inherit(&self) -> io::Result<()> {
        Err(io::Error::new(io::ErrorKind::Other, "Unavailable on UWP"))
    }

    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        let how = match how {
            Shutdown::Write => c::SD_SEND,
            Shutdown::Read => c::SD_RECEIVE,
            Shutdown::Both => c::SD_BOTH,
        };
        cvt(unsafe { c::shutdown(self.0, how) })?;
        Ok(())
    }

    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        let mut nonblocking = nonblocking as c_ulong;
        let r = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) };
        if r == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }

    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
        net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE)
    }

    pub fn nodelay(&self) -> io::Result<bool> {
        let raw: c::BYTE = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?;
        Ok(raw != 0)
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?;
        if raw == 0 {
            Ok(None)
        } else {
            Ok(Some(io::Error::from_raw_os_error(raw as i32)))
        }
    }
}

#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
impl<'a> Read for &'a Socket {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }
}

impl Drop for Socket {
    fn drop(&mut self) {
        let _ = unsafe { c::closesocket(self.0) };
    }
}

impl AsInner<c::SOCKET> for Socket {
    fn as_inner(&self) -> &c::SOCKET { &self.0 }
}

impl FromInner<c::SOCKET> for Socket {
    fn from_inner(sock: c::SOCKET) -> Socket { Socket(sock) }
}

impl IntoInner<c::SOCKET> for Socket {
    fn into_inner(self) -> c::SOCKET {
        let ret = self.0;
        mem::forget(self);
        ret
    }
}
<commit_msg>added correct error code for WSASocketW failure fallback<commit_after>#![unstable(issue = "0", feature = "windows_net")]

use crate::cmp;
use crate::io::{self, Read, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{SocketAddr, Shutdown};
use crate::ptr;
use crate::sync::Once;
use crate::sys::c;
use crate::sys;
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
use crate::sys_common::net;
use crate::time::Duration;

use libc::{c_int, c_void, c_ulong, c_long};

pub type wrlen_t = i32;

pub mod netc {
    pub use crate::sys::c::*;
    pub use crate::sys::c::SOCKADDR as sockaddr;
    pub use crate::sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage;
    pub use crate::sys::c::ADDRINFOA as addrinfo;
    pub use crate::sys::c::ADDRESS_FAMILY as sa_family_t;
}

pub struct Socket(c::SOCKET);

/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
pub fn init() {
    static START: Once = Once::new();

    START.call_once(|| unsafe {
        let mut data: c::WSADATA = mem::zeroed();
        let ret = c::WSAStartup(0x202, // version 2.2
                                &mut data);
        assert_eq!(ret, 0);

        let _ = sys_common::at_exit(|| { c::WSACleanup(); });
    });
}

/// Returns the last error from the Windows socket interface.
fn last_error() -> io::Error {
    io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
/// and if so, returns the last error from the Windows socket interface. This
/// function must be called before another call to the socket API is made.
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
    if t.is_minus_one() {
        Err(last_error())
    } else {
        Ok(t)
    }
}

/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
pub fn cvt_gai(err: c_int) -> io::Result<()> {
    if err == 0 {
        Ok(())
    } else {
        Err(last_error())
    }
}

/// Just to provide the same interface as sys/unix/net.rs
pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
    where T: IsMinusOne,
          F: FnMut() -> T
{
    cvt(f())
}

impl Socket {
    pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
        let fam = match *addr {
            SocketAddr::V4(..) => c::AF_INET,
            SocketAddr::V6(..) => c::AF_INET6,
        };
        let socket = unsafe {
            match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0,
                                c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) {
                c::INVALID_SOCKET => {
                    match c::WSAGetLastError() {
                        c::WSAEPROTOTYPE | c::WSAEINVAL => {
                            match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0,
                                                c::WSA_FLAG_OVERLAPPED) {
                                c::INVALID_SOCKET => Err(last_error()),
                                n => {
                                    let s = Socket(n);
                                    s.set_no_inherit()?;
                                    Ok(s)
                                },
                            }
                        },
                        n => Err(io::Error::from_raw_os_error(n)),
                    }
                },
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
        self.set_nonblocking(true)?;
        let r = unsafe {
            let (addrp, len) = addr.into_inner();
            cvt(c::connect(self.0, addrp, len))
        };
        self.set_nonblocking(false)?;

        match r {
            Ok(_) => return Ok(()),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
            Err(e) => return Err(e),
        }

        if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
            return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                      "cannot set a 0 duration timeout"));
        }

        let mut timeout = c::timeval {
            tv_sec: timeout.as_secs() as c_long,
            tv_usec: (timeout.subsec_nanos() / 1000) as c_long,
        };
        if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
            timeout.tv_usec = 1;
        }

        let fds = unsafe {
            let mut fds = mem::zeroed::<c::fd_set>();
            fds.fd_count = 1;
            fds.fd_array[0] = self.0;
            fds
        };

        let mut writefds = fds;
        let mut errorfds = fds;

        let n = unsafe {
            cvt(c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout))?
        };

        match n {
            0 => Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")),
            _ => {
                if writefds.fd_count != 1 {
                    if let Some(e) = self.take_error()? {
                        return Err(e);
                    }
                }
                Ok(())
            }
        }
    }

    pub fn accept(&self, storage: *mut c::SOCKADDR,
                  len: *mut c_int) -> io::Result<Socket> {
        let socket = unsafe {
            match c::accept(self.0, storage, len) {
                c::INVALID_SOCKET => Err(last_error()),
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    pub fn duplicate(&self) -> io::Result<Socket> {
        let socket = unsafe {
            let mut info: c::WSAPROTOCOL_INFO = mem::zeroed();
            cvt(c::WSADuplicateSocketW(self.0,
                                            c::GetCurrentProcessId(),
                                            &mut info))?;

            match c::WSASocketW(info.iAddressFamily,
                                info.iSocketType,
                                info.iProtocol,
                                &mut info, 0,
                                c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) {
                c::INVALID_SOCKET => {
                    match c::WSAGetLastError() {
                        c::WSAEPROTOTYPE | c::WSAEINVAL => {
                            match c::WSASocketW(info.iAddressFamily,
                                                info.iSocketType,
                                                info.iProtocol,
                                                &mut info, 0,
                                                c::WSA_FLAG_OVERLAPPED) {
                                c::INVALID_SOCKET => Err(last_error()),
                                n => {
                                    let s = Socket(n);
                                    s.set_no_inherit()?;
                                    Ok(s)
                                },
                            }
                        },
                        n => Err(io::Error::from_raw_os_error(n)),
                    }
                },
                n => Ok(Socket(n)),
            }
        }?;
        Ok(socket)
    }

    fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        let len = cmp::min(buf.len(), i32::max_value() as usize) as i32;
        unsafe {
            match c::recv(self.0, buf.as_mut_ptr() as *mut c_void, len, flags) {
                -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0),
                -1 => Err(last_error()),
                n => Ok(n as usize)
            }
        }
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        self.recv_with_flags(buf, 0)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
        let mut nread = 0;
        let mut flags = 0;
        unsafe {
            let ret = c::WSARecv(
                self.0,
                bufs.as_mut_ptr() as *mut c::WSABUF,
                len,
                &mut nread,
                &mut flags,
                ptr::null_mut(),
                ptr::null_mut(),
            );
            match ret {
                0 => Ok(nread as usize),
                _ if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0),
                _ => Err(last_error()),
            }
        }
    }

    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
        self.recv_with_flags(buf, c::MSG_PEEK)
    }

    fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int)
                            -> io::Result<(usize, SocketAddr)> {
        let mut storage: c::SOCKADDR_STORAGE_LH = unsafe { mem::zeroed() };
        let mut addrlen = mem::size_of_val(&storage) as c::socklen_t;
        let len = cmp::min(buf.len(), <wrlen_t>::max_value() as usize) as wrlen_t;

        // On unix when a socket is shut down all further reads return 0, so we
        // do the same on windows to map a shut down socket to returning EOF.
        unsafe {
            match c::recvfrom(self.0,
                              buf.as_mut_ptr() as *mut c_void,
                              len,
                              flags,
                              &mut storage as *mut _ as *mut _,
                              &mut addrlen) {
                -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => {
                    Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?))
                },
                -1 => Err(last_error()),
                n => Ok((n as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)),
            }
        }
    }

    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        self.recv_from_with_flags(buf, 0)
    }

    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        self.recv_from_with_flags(buf, c::MSG_PEEK)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD;
        let mut nwritten = 0;
        unsafe {
            cvt(c::WSASend(
                self.0,
                bufs.as_ptr() as *const c::WSABUF as *mut c::WSABUF,
                len,
                &mut nwritten,
                0,
                ptr::null_mut(),
                ptr::null_mut(),
            ))?;
        }
        Ok(nwritten as usize)
    }

    pub fn set_timeout(&self, dur: Option<Duration>,
                       kind: c_int) -> io::Result<()> {
        let timeout = match dur {
            Some(dur) => {
                let timeout = sys::dur2timeout(dur);
                if timeout == 0 {
                    return Err(io::Error::new(io::ErrorKind::InvalidInput,
                                              "cannot set a 0 duration timeout"));
                }
                timeout
            }
            None => 0
        };
        net::setsockopt(self, c::SOL_SOCKET, kind, timeout)
    }

    pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
        let raw: c::DWORD = net::getsockopt(self, c::SOL_SOCKET, kind)?;
        if raw == 0 {
            Ok(None)
        } else {
            let secs = raw / 1000;
            let nsec = (raw % 1000) * 1000000;
            Ok(Some(Duration::new(secs as u64, nsec as u32)))
        }
    }

    #[cfg(not(target_vendor = "uwp"))]
    fn set_no_inherit(&self) -> io::Result<()> {
        sys::cvt(unsafe {
            c::SetHandleInformation(self.0 as c::HANDLE,
                                    c::HANDLE_FLAG_INHERIT, 0)
        }).map(|_| ())
    }

    #[cfg(target_vendor = "uwp")]
    fn set_no_inherit(&self) -> io::Result<()> {
        Err(io::Error::new(io::ErrorKind::Other, "Unavailable on UWP"))
    }

    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        let how = match how {
            Shutdown::Write => c::SD_SEND,
            Shutdown::Read => c::SD_RECEIVE,
            Shutdown::Both => c::SD_BOTH,
        };
        cvt(unsafe { c::shutdown(self.0, how) })?;
        Ok(())
    }

    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        let mut nonblocking = nonblocking as c_ulong;
        let r = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) };
        if r == 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }

    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
        net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE)
    }

    pub fn nodelay(&self) -> io::Result<bool> {
        let raw: c::BYTE = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?;
        Ok(raw != 0)
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?;
        if raw == 0 {
            Ok(None)
        } else {
            Ok(Some(io::Error::from_raw_os_error(raw as i32)))
        }
    }
}

#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
impl<'a> Read for &'a Socket {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }
}

impl Drop for Socket {
    fn drop(&mut self) {
        let _ = unsafe { c::closesocket(self.0) };
    }
}

impl AsInner<c::SOCKET> for Socket {
    fn as_inner(&self) -> &c::SOCKET { &self.0 }
}

impl FromInner<c::SOCKET> for Socket {
    fn from_inner(sock: c::SOCKET) -> Socket { Socket(sock) }
}

impl IntoInner<c::SOCKET> for Socket {
    fn into_inner(self) -> c::SOCKET {
        let ret = self.0;
        mem::forget(self);
        ret
    }
}
<|endoftext|>


<commit_before>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
// To run tests only for a particular package, while showing the operations, you could use:
// `cargo run --example syntest -- --debug testdata/Packages/Makefile/`
// to specify that the syntax definitions should be parsed instead of loaded from the dump file,
// you can tell it where to parse them from - the following will execute only 1 syntax test after
// parsing the sublime-syntax files in the JavaScript folder:
// `cargo run --example syntest testdata/Packages/JavaScript/syntax_test_json.json testdata/Packages/JavaScript/`
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.*)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str,
    is_pure_assertion_line: bool,
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);

        if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
            let mut sst = captures.get(3).unwrap().as_str(); // get the scope selector text
            let mut only_whitespace_after_token_end = true;

            if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                if let Some(end_token_pos) = sst.find(token) { // and there is an end token in the line
                    let (ss, after_token_end) = sst.split_at(end_token_pos); // the scope selector text ends at the end token
                    sst = &ss;
                    only_whitespace_after_token_end = after_token_end.trim_right().is_empty();
                }
            }
            return Some(AssertionRange {
                begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                scope_selector_text: sst,
                is_pure_assertion_line: before_token_start.trim_left().is_empty() && only_whitespace_after_token_end, // if only whitespace surrounds the test tokens on the line, then it is a pure assertion line
            });
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    // format the scope selector to include a space at the beginning, because, currently, ScopeSelector expects excludes to begin with " -"
    // and they are sometimes in the syntax test as ^^^-comment, for example
    let selector = ScopeSelectors::from_str(&format!(" {}", &assertion.scope_selector_text)).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

/// If `parse_test_lines` is `false` then lines that only contain assertions are not parsed
fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool, debug: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    use syntect::util::debug_print_ops;
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    let header_line = line.clone();
    let search_result = SYNTAX_TEST_HEADER_PATTERN.captures(&header_line);
    let captures = search_result.ok_or(SyntaxTestHeaderError::MalformedHeader)?;

    let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
    let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
    let syntax_file = captures.name("syntax_file").unwrap().as_str();

    // find the relevant syntax definition to parse the file with - case is important!
    println!("The test file references syntax definition file: {}", syntax_file);
    let syntax = ss.find_syntax_by_path(syntax_file).ok_or(SyntaxTestHeaderError::SyntaxDefinitionNotFound)?;

    // iterate over the lines of the file, testing them
    let mut state = ParseState::new(syntax);
    let mut stack = ScopeStack::new();

    let mut current_line_number = 1;
    let mut test_against_line_number = 1;
    let mut scopes_on_line_being_tested = Vec::new();
    let mut previous_non_assertion_line = line.to_string();

    let mut assertion_failures: usize = 0;
    let mut total_assertions: usize = 0;

    loop { // over lines of file, starting with the header line
        let mut line_only_has_assertion = false;
        let mut line_has_assertion = false;
        if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
            let result = process_assertions(&assertion, &scopes_on_line_being_tested);
            total_assertions += &assertion.end_char - &assertion.begin_char;
            for failure in result.iter().filter(|r|!r.success) {
                let length = failure.column_end - failure.column_begin;
                let text: String = previous_non_assertion_line.chars().skip(failure.column_begin).take(length).collect();
                println!("  Assertion selector {:?} \
                    from line {:?} failed against line {:?}, column range {:?}-{:?} \
                    (with text {:?}) \
                    has scope {:?}",
                    assertion.scope_selector_text.trim(),
                    current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                    text,
                    scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                );
                assertion_failures += failure.column_end - failure.column_begin;
            }
            line_only_has_assertion = assertion.is_pure_assertion_line;
            line_has_assertion = true;
        }
        if !line_only_has_assertion || parse_test_lines {
            if !line_has_assertion { // ST seems to ignore lines that have assertions when calculating which line the assertion tests against
                scopes_on_line_being_tested.clear();
                test_against_line_number = current_line_number;
                previous_non_assertion_line = line.to_string();
            }
            if debug && !line_only_has_assertion {
                println!("-- debugging line {} -- scope stack: {:?} -- contexts on the stack: {:?}", current_line_number, stack, state);
            }
            let ops = state.parse_line(&line);
            if debug && !line_only_has_assertion {
                debug_print_ops(&line, &ops);
            }
            let mut col: usize = 0;
            for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                stack.apply(op);
                if s.is_empty() { // in this case we don't care about blank tokens
                    continue;
                }
                if !line_has_assertion {
                    // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                    let len = s.chars().count();
                    scopes_on_line_being_tested.push(
                        ScopedText {
                            char_start: col,
                            text_len: len,
                            scope: stack.as_slice().to_vec()
                        }
                    );
                    // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                    col += len;
                }
            }
        }

        line.clear();
        current_line_number += 1;
        if reader.read_line(&mut line).unwrap() == 0 {
            break;
        }
        line = line.replace("\r", &"");
    }
    if assertion_failures > 0 {
        Ok(SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions))
    } else {
        Ok(SyntaxTestFileResult::Success(total_assertions))
    }
}

fn main() {
    let mut args: Vec<String> = std::env::args().collect();
    let debug_arg = args.iter().position(|s| s == "--debug");
    if debug_arg.is_some() {
        args.remove(debug_arg.unwrap());
    }

    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path, debug_arg.is_some());
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str, debug: bool) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true, debug);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<commit_msg>small tweak to syntest debug output<commit_after>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
// To run tests only for a particular package, while showing the operations, you could use:
// cargo run --example syntest -- --debug testdata/Packages/Makefile/
// to specify that the syntax definitions should be parsed instead of loaded from the dump file,
// you can tell it where to parse them from - the following will execute only 1 syntax test after
// parsing the sublime-syntax files in the JavaScript folder:
// cargo run --example syntest testdata/Packages/JavaScript/syntax_test_json.json testdata/Packages/JavaScript/
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.*)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str,
    is_pure_assertion_line: bool,
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);

        if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
            let mut sst = captures.get(3).unwrap().as_str(); // get the scope selector text
            let mut only_whitespace_after_token_end = true;

            if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                if let Some(end_token_pos) = sst.find(token) { // and there is an end token in the line
                    let (ss, after_token_end) = sst.split_at(end_token_pos); // the scope selector text ends at the end token
                    sst = &ss;
                    only_whitespace_after_token_end = after_token_end.trim_right().is_empty();
                }
            }
            return Some(AssertionRange {
                begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                scope_selector_text: sst,
                is_pure_assertion_line: before_token_start.trim_left().is_empty() && only_whitespace_after_token_end, // if only whitespace surrounds the test tokens on the line, then it is a pure assertion line
            });
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    // format the scope selector to include a space at the beginning, because, currently, ScopeSelector expects excludes to begin with " -"
    // and they are sometimes in the syntax test as ^^^-comment, for example
    let selector = ScopeSelectors::from_str(&format!(" {}", &assertion.scope_selector_text)).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

/// If `parse_test_lines` is `false` then lines that only contain assertions are not parsed
fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool, debug: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    use syntect::util::debug_print_ops;
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    let header_line = line.clone();
    let search_result = SYNTAX_TEST_HEADER_PATTERN.captures(&header_line);
    let captures = search_result.ok_or(SyntaxTestHeaderError::MalformedHeader)?;

    let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
    let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
    let syntax_file = captures.name("syntax_file").unwrap().as_str();

    // find the relevant syntax definition to parse the file with - case is important!
    println!("The test file references syntax definition file: {}", syntax_file);
    let syntax = ss.find_syntax_by_path(syntax_file).ok_or(SyntaxTestHeaderError::SyntaxDefinitionNotFound)?;

    // iterate over the lines of the file, testing them
    let mut state = ParseState::new(syntax);
    let mut stack = ScopeStack::new();

    let mut current_line_number = 1;
    let mut test_against_line_number = 1;
    let mut scopes_on_line_being_tested = Vec::new();
    let mut previous_non_assertion_line = line.to_string();

    let mut assertion_failures: usize = 0;
    let mut total_assertions: usize = 0;

    loop { // over lines of file, starting with the header line
        let mut line_only_has_assertion = false;
        let mut line_has_assertion = false;
        if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
            let result = process_assertions(&assertion, &scopes_on_line_being_tested);
            total_assertions += &assertion.end_char - &assertion.begin_char;
            for failure in result.iter().filter(|r|!r.success) {
                let length = failure.column_end - failure.column_begin;
                let text: String = previous_non_assertion_line.chars().skip(failure.column_begin).take(length).collect();
                println!("  Assertion selector {:?} \
                    from line {:?} failed against line {:?}, column range {:?}-{:?} \
                    (with text {:?}) \
                    has scope {:?}",
                    assertion.scope_selector_text.trim(),
                    current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                    text,
                    scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                );
                assertion_failures += failure.column_end - failure.column_begin;
            }
            line_only_has_assertion = assertion.is_pure_assertion_line;
            line_has_assertion = true;
        }
        if !line_only_has_assertion || parse_test_lines {
            if !line_has_assertion { // ST seems to ignore lines that have assertions when calculating which line the assertion tests against
                scopes_on_line_being_tested.clear();
                test_against_line_number = current_line_number;
                previous_non_assertion_line = line.to_string();
            }
            if debug && !line_only_has_assertion {
                println!("-- debugging line {} -- scope stack: {:?}", current_line_number, stack);
            }
            let ops = state.parse_line(&line);
            if debug && !line_only_has_assertion {
                if ops.is_empty() && !line.is_empty() {
                    println!("no operations for this line...");
                } else {
                    debug_print_ops(&line, &ops);
                }
            }
            let mut col: usize = 0;
            for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                stack.apply(op);
                if s.is_empty() { // in this case we don't care about blank tokens
                    continue;
                }
                if !line_has_assertion {
                    // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                    let len = s.chars().count();
                    scopes_on_line_being_tested.push(
                        ScopedText {
                            char_start: col,
                            text_len: len,
                            scope: stack.as_slice().to_vec()
                        }
                    );
                    // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                    col += len;
                }
            }
        }

        line.clear();
        current_line_number += 1;
        if reader.read_line(&mut line).unwrap() == 0 {
            break;
        }
        line = line.replace("\r", &"");
    }
    if assertion_failures > 0 {
        Ok(SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions))
    } else {
        Ok(SyntaxTestFileResult::Success(total_assertions))
    }
}

fn main() {
    let mut args: Vec<String> = std::env::args().collect();
    let debug_arg = args.iter().position(|s| s == "--debug");
    if debug_arg.is_some() {
        args.remove(debug_arg.unwrap());
    }

    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path, debug_arg.is_some());
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str, debug: bool) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true, debug);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<|endoftext|>


<commit_before>use linkify::{LinkFinder, LinkKind};
use regex::Regex;
use std::borrow::Cow;

pub fn linkify_comment(s: String) -> String {
    let mut finder = LinkFinder::new();
    finder.kinds(&[LinkKind::Url]);

    let mut last = 0;
    let mut result = String::new();
    for link in finder.links(&s) {
        result.push_str(&linkify_bug_numbers(&s[last..link.start()]));
        result.push_str(&format!(
            "{}",
            link.as_str(),
            linkify_bug_numbers(link.as_str())
        ));
        last = link.end();
    }

    if last == 0 {
        return linkify_bug_numbers(&s).into_owned();
    }

    result.push_str(&linkify_bug_numbers(&s[last..]));
    result
}

fn linkify_bug_numbers(s: &str) -> Cow<str> {
    lazy_static! {
        static ref BUG_NUMBER_REGEX: Regex =
            { Regex::new(r"\b(?i)bug\s*(?P<bugno>[1-9][0-9]{2,6})\b").unwrap() };
    }
    BUG_NUMBER_REGEX.replace_all(
        s,
        "$0",
    )
}

pub fn linkify_commit_header(s: &str) -> String {
    lazy_static! {
        static ref BUG_NUMBER_REGEX: Regex =
            { Regex::new(r"\b(?P<bugno>[1-9][0-9]{4,9})\b").unwrap() };
        static ref SERVO_PR_REGEX: Regex = { Regex::new(r"#(?P<prno>[1-9][0-9]*)\b").unwrap() };
    }
    if s.starts_with("servo: ") {
        SERVO_PR_REGEX
            .replace_all(
                s,
                "#$prno",
            )
            .into_owned()
    } else {
        BUG_NUMBER_REGEX
            .replace_all(
                s,
                "$bugno",
            )
            .into_owned()
    }
}

#[test]
fn test_linkify_servo_pr() {
    let linkified =
        linkify_commit_header("servo: Merge #1234 - stylo: Report a specific error for invalid CSS color values (from jdm:valueerr); r=heycam");
    assert!(linkified.contains("github.com"), "{:?}", linkified);
}

#[test]
fn test_bug_number() {
    let linkified = linkify_bug_numbers("this is a link to bUg 12345");
    assert!(
        linkified.contains("bugzilla.mozilla.org"),
        "{:?}",
        linkified
    );
    assert!(linkified.contains(">bUg 12345"), "{:?}", linkified);
}

#[test]
fn test_bug_number_inside_link() {
    let link = "http://example.org/browser/editor/libeditor/tests/bug629172.html";
    let linkified = linkify_comment(link.into());
    assert_eq!(linkified, "http://example.org/browser/editor/libeditor/tests/bug629172.html");
}
<commit_msg>links: Remove unnecessary braces.<commit_after>use linkify::{LinkFinder, LinkKind};
use regex::Regex;
use std::borrow::Cow;

pub fn linkify_comment(s: String) -> String {
    let mut finder = LinkFinder::new();
    finder.kinds(&[LinkKind::Url]);

    let mut last = 0;
    let mut result = String::new();
    for link in finder.links(&s) {
        result.push_str(&linkify_bug_numbers(&s[last..link.start()]));
        result.push_str(&format!(
            "{}",
            link.as_str(),
            linkify_bug_numbers(link.as_str())
        ));
        last = link.end();
    }

    if last == 0 {
        return linkify_bug_numbers(&s).into_owned();
    }

    result.push_str(&linkify_bug_numbers(&s[last..]));
    result
}

fn linkify_bug_numbers(s: &str) -> Cow<str> {
    lazy_static! {
        static ref BUG_NUMBER_REGEX: Regex =
            Regex::new(r"\b(?i)bug\s*(?P<bugno>[1-9][0-9]{2,6})\b").unwrap();
    }
    BUG_NUMBER_REGEX.replace_all(
        s,
        "$0",
    )
}

pub fn linkify_commit_header(s: &str) -> String {
    lazy_static! {
        static ref BUG_NUMBER_REGEX: Regex =
            Regex::new(r"\b(?P<bugno>[1-9][0-9]{4,9})\b").unwrap();
        static ref SERVO_PR_REGEX: Regex = Regex::new(r"#(?P<prno>[1-9][0-9]*)\b").unwrap();
    }
    if s.starts_with("servo: ") {
        SERVO_PR_REGEX
            .replace_all(
                s,
                "#$prno",
            )
            .into_owned()
    } else {
        BUG_NUMBER_REGEX
            .replace_all(
                s,
                "$bugno",
            )
            .into_owned()
    }
}

#[test]
fn test_linkify_servo_pr() {
    let linkified =
        linkify_commit_header("servo: Merge #1234 - stylo: Report a specific error for invalid CSS color values (from jdm:valueerr); r=heycam");
    assert!(linkified.contains("github.com"), "{:?}", linkified);
}

#[test]
fn test_bug_number() {
    let linkified = linkify_bug_numbers("this is a link to bUg 12345");
    assert!(
        linkified.contains("bugzilla.mozilla.org"),
        "{:?}",
        linkified
    );
    assert!(linkified.contains(">bUg 12345"), "{:?}", linkified);
}

#[test]
fn test_bug_number_inside_link() {
    let link = "http://example.org/browser/editor/libeditor/tests/bug629172.html";
    let linkified = linkify_comment(link.into());
    assert_eq!(linkified, "http://example.org/browser/editor/libeditor/tests/bug629172.html");
}
<|endoftext|>


<commit_before>//! Common utilities for the generator and the build script for Qt crates.
//!
//! See [README](https://github.com/rust-qt/ritual) of the repository root for more information.

use log::debug;
use ritual_common::cpp_build_config::{
    CppBuildConfig, CppBuildConfigData, CppBuildPaths, CppLibraryType,
};
use ritual_common::cpp_lib_builder::CMakeVar;
use ritual_common::errors::{bail, Result};
use ritual_common::string_utils::CaseOperations;
use ritual_common::target;
use ritual_common::utils::get_command_output;
use std::path::PathBuf;
use std::process::Command;

/// Makes a query to `qmake`.
fn run_qmake_string_query(property: &str, qmake_path: Option<&str>) -> Result<String> {
    let command = qmake_path.unwrap_or("qmake");
    let result = get_command_output(Command::new(command).arg("-query").arg(property))?;
    Ok(result.trim().to_string())
}

/// Makes a query to `qmake` and interprets its output as a path.
fn run_qmake_query(property: &str, qmake_path: Option<&str>) -> Result<PathBuf> {
    Ok(PathBuf::from(run_qmake_string_query(property, qmake_path)?))
}

/// Properties of a Qt installation
pub struct InstallationData {
    /// Qt version.
    pub qt_version: String,
    /// Path to the parent include directory of the installation.
    pub root_include_path: PathBuf,
    /// Path to the include directory of the library that is being processed.
    /// This is a direct subdirectory of `root_include_path`.
    pub lib_include_path: PathBuf,
    /// Path to the directory containing library files for the linker.
    pub lib_path: PathBuf,
    /// Path to the directory containing Qt documentation files.
    pub docs_path: PathBuf,
    /// If true, this Qt library was built as a MacOS framework.
    pub is_framework: bool,
}

/// Detects properties of current Qt installation using `qmake` command line utility.
pub fn get_installation_data(
    crate_name: &str,
    qmake_path: Option<&str>,
) -> Result<InstallationData> {
    let qt_version = run_qmake_string_query("QT_VERSION", qmake_path)?;
    debug!("QT_VERSION = \"{}\"", qt_version);

    let qt_version_parsed = semver::Version::parse(&qt_version)?;
    if qt_version_parsed.major != 5 {
        bail!(
            "only Qt 5 is supported! \"{}\" is not supported",
            qt_version
        );
    }

    debug!("Detecting Qt directories");

    let root_include_path = run_qmake_query("QT_INSTALL_HEADERS", qmake_path)?;
    debug!("QT_INSTALL_HEADERS = \"{}\"", root_include_path.display());
    let lib_path = run_qmake_query("QT_INSTALL_LIBS", qmake_path)?;
    debug!("QT_INSTALL_LIBS = \"{}\"", lib_path.display());
    let docs_path = run_qmake_query("QT_INSTALL_DOCS", qmake_path)?;
    debug!("QT_INSTALL_DOCS = \"{}\"", docs_path.display());
    let folder_name = lib_folder_name(crate_name);
    let dir = root_include_path.join(&folder_name);
    if dir.exists() {
        Ok(InstallationData {
            root_include_path,
            lib_path,
            docs_path,
            lib_include_path: dir,
            is_framework: false,
            qt_version,
        })
    } else {
        let dir2 = lib_path.join(format!("{}.framework/Headers", folder_name));
        if dir2.exists() {
            Ok(InstallationData {
                root_include_path,
                lib_path,
                docs_path,
                lib_include_path: dir2,
                is_framework: true,
                qt_version,
            })
        } else {
            bail!(
                "extra header dir not found (tried: {}, {})",
                dir.display(),
                dir2.display()
            );
        }
    }
}

pub struct FullBuildConfig {
    pub installation_data: InstallationData,
    pub cpp_build_config: CppBuildConfig,
    pub cpp_build_paths: CppBuildPaths,
}

pub fn get_full_build_config(
    crate_name: &str,
    qmake_path: Option<&str>,
) -> Result<FullBuildConfig> {
    let installation_data = get_installation_data(crate_name, qmake_path)?;
    let mut cpp_build_paths = CppBuildPaths::new();
    let mut cpp_build_config_data = CppBuildConfigData::new();

    let mut apply_installation_data = |name: &str, data: &InstallationData| {
        cpp_build_paths.add_include_path(&data.root_include_path);
        cpp_build_paths.add_include_path(&data.lib_include_path);
        if data.is_framework {
            cpp_build_paths.add_framework_path(&data.lib_path);
            cpp_build_config_data.add_linked_framework(framework_name(name));
        } else {
            cpp_build_paths.add_lib_path(&data.lib_path);
            cpp_build_config_data.add_linked_lib(real_lib_name(name));
        }
        cpp_build_config_data.add_cmake_var(CMakeVar::new("RITUAL_QT", "1"));
    };

    apply_installation_data(crate_name, &installation_data);
    for dep in lib_dependencies(crate_name)? {
        let dep_data = get_installation_data(dep, qmake_path)?;
        apply_installation_data(dep, &dep_data);
    }

    let mut cpp_build_config = CppBuildConfig::new();
    cpp_build_config.add(target::Condition::True, cpp_build_config_data);
    {
        let mut data = CppBuildConfigData::new();
        data.add_compiler_flag("-std=gnu++11");
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc).negate(), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.add_compiler_flag("-fPIC");
        // msvc and mingw don't need this
        cpp_build_config.add(target::Condition::OS(target::OS::Windows).negate(), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.set_library_type(CppLibraryType::Shared);
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.set_library_type(CppLibraryType::Static);
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc).negate(), data);
    }
    Ok(FullBuildConfig {
        installation_data,
        cpp_build_config,
        cpp_build_paths,
    })
}

/// Returns library name of the specified module as
/// should be passed to the linker, e.g. `"Qt5Core"`.
pub fn real_lib_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt5{}", sublib_name_capitalized)
}

/// Returns name of the module's include directory, e.g. `"QtCore"`.
pub fn lib_folder_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt{}", sublib_name_capitalized)
}

/// Returns MacOS framework name of the specified module as
/// should be passed to the linker, e.g. `"QtCore"`.
pub fn framework_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt{}", sublib_name_capitalized)
}

pub fn all_crate_names() -> &'static [&'static str] {
    &[
        "qt_core",
        "qt_gui",
        "qt_widgets",
        "qt_ui_tools",
        "qt_3d_core",
        "qt_3d_render",
        "qt_3d_input",
        "qt_3d_logic",
        "qt_3d_extras",
        "qt_charts",
        "qt_qml",
    ]
}

/// Returns list of modules this module depends on.
pub fn lib_dependencies(crate_name: &str) -> Result<&'static [&'static str]> {
    Ok(match crate_name {
        "qt_core" => &[],
        "qt_gui" => &["qt_core"],
        "qt_widgets" => &["qt_core", "qt_gui"],
        "qt_3d_core" => &["qt_core", "qt_gui"],
        "qt_3d_render" | "qt_3d_input" | "qt_3d_logic" => &["qt_core", "qt_gui", "qt_3d_core"],
        "qt_3d_extras" => &[
            "qt_core",
            "qt_gui",
            "qt_3d_core",
            "qt_3d_render",
            "qt_3d_input",
            "qt_3d_logic",
        ],
        "qt_ui_tools" | "qt_charts" => &["qt_core", "qt_gui", "qt_widgets"],
        // NOTE: qt_qml actually depends on qt_network as well
        "qt_qml" => &["qt_core", "qt_gui"],
        "moqt_core" => &[],
        "moqt_gui" => &["moqt_core"],
        _ => bail!("Unknown crate name: {}", crate_name),
    })
}
<commit_msg>fix framework detection again<commit_after>//! Common utilities for the generator and the build script for Qt crates.
//!
//! See [README](https://github.com/rust-qt/ritual) of the repository root for more information.

use log::debug;
use ritual_common::cpp_build_config::{
    CppBuildConfig, CppBuildConfigData, CppBuildPaths, CppLibraryType,
};
use ritual_common::cpp_lib_builder::CMakeVar;
use ritual_common::errors::{bail, Result};
use ritual_common::string_utils::CaseOperations;
use ritual_common::target;
use ritual_common::utils::get_command_output;
use std::path::PathBuf;
use std::process::Command;

/// Makes a query to `qmake`.
fn run_qmake_string_query(property: &str, qmake_path: Option<&str>) -> Result<String> {
    let command = qmake_path.unwrap_or("qmake");
    let result = get_command_output(Command::new(command).arg("-query").arg(property))?;
    Ok(result.trim().to_string())
}

/// Makes a query to `qmake` and interprets its output as a path.
fn run_qmake_query(property: &str, qmake_path: Option<&str>) -> Result<PathBuf> {
    Ok(PathBuf::from(run_qmake_string_query(property, qmake_path)?))
}

/// Properties of a Qt installation
pub struct InstallationData {
    /// Qt version.
    pub qt_version: String,
    /// Path to the parent include directory of the installation.
    pub root_include_path: PathBuf,
    /// Path to the include directory of the library that is being processed.
    /// This is a direct subdirectory of `root_include_path`.
    pub lib_include_path: PathBuf,
    /// Path to the directory containing library files for the linker.
    pub lib_path: PathBuf,
    /// Path to the directory containing Qt documentation files.
    pub docs_path: PathBuf,
    /// If true, this Qt library was built as a MacOS framework.
    pub is_framework: bool,
}

/// Detects properties of current Qt installation using `qmake` command line utility.
pub fn get_installation_data(
    crate_name: &str,
    qmake_path: Option<&str>,
) -> Result<InstallationData> {
    let qt_version = run_qmake_string_query("QT_VERSION", qmake_path)?;
    debug!("QT_VERSION = \"{}\"", qt_version);

    let qt_version_parsed = semver::Version::parse(&qt_version)?;
    if qt_version_parsed.major != 5 {
        bail!(
            "only Qt 5 is supported! \"{}\" is not supported",
            qt_version
        );
    }

    debug!("Detecting Qt directories");

    let root_include_path = run_qmake_query("QT_INSTALL_HEADERS", qmake_path)?;
    debug!("QT_INSTALL_HEADERS = \"{}\"", root_include_path.display());
    let lib_path = run_qmake_query("QT_INSTALL_LIBS", qmake_path)?;
    debug!("QT_INSTALL_LIBS = \"{}\"", lib_path.display());
    let docs_path = run_qmake_query("QT_INSTALL_DOCS", qmake_path)?;
    debug!("QT_INSTALL_DOCS = \"{}\"", docs_path.display());
    let folder_name = lib_folder_name(crate_name);

    let framework_headers_dir = lib_path.join(format!("{}.framework/Headers", folder_name));
    if framework_headers_dir.exists() {
        Ok(InstallationData {
            root_include_path,
            lib_path,
            docs_path,
            lib_include_path: framework_headers_dir,
            is_framework: true,
            qt_version,
        })
    } else {
        let lib_headers_dir = root_include_path.join(&folder_name);
        if lib_headers_dir.exists() {
            Ok(InstallationData {
                root_include_path,
                lib_path,
                docs_path,
                lib_include_path: lib_headers_dir,
                is_framework: false,
                qt_version,
            })
        } else {
            bail!(
                "extra header dir not found (tried: {}, {})",
                framework_headers_dir.display(),
                lib_headers_dir.display()
            );
        }
    }
}

pub struct FullBuildConfig {
    pub installation_data: InstallationData,
    pub cpp_build_config: CppBuildConfig,
    pub cpp_build_paths: CppBuildPaths,
}

pub fn get_full_build_config(
    crate_name: &str,
    qmake_path: Option<&str>,
) -> Result<FullBuildConfig> {
    let installation_data = get_installation_data(crate_name, qmake_path)?;
    let mut cpp_build_paths = CppBuildPaths::new();
    let mut cpp_build_config_data = CppBuildConfigData::new();

    let mut apply_installation_data = |name: &str, data: &InstallationData| {
        cpp_build_paths.add_include_path(&data.root_include_path);
        cpp_build_paths.add_include_path(&data.lib_include_path);
        if data.is_framework {
            cpp_build_paths.add_framework_path(&data.lib_path);
            cpp_build_config_data.add_linked_framework(framework_name(name));
        } else {
            cpp_build_paths.add_lib_path(&data.lib_path);
            cpp_build_config_data.add_linked_lib(real_lib_name(name));
        }
        cpp_build_config_data.add_cmake_var(CMakeVar::new("RITUAL_QT", "1"));
    };

    apply_installation_data(crate_name, &installation_data);
    for dep in lib_dependencies(crate_name)? {
        let dep_data = get_installation_data(dep, qmake_path)?;
        apply_installation_data(dep, &dep_data);
    }

    let mut cpp_build_config = CppBuildConfig::new();
    cpp_build_config.add(target::Condition::True, cpp_build_config_data);
    {
        let mut data = CppBuildConfigData::new();
        data.add_compiler_flag("-std=gnu++11");
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc).negate(), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.add_compiler_flag("-fPIC");
        // msvc and mingw don't need this
        cpp_build_config.add(target::Condition::OS(target::OS::Windows).negate(), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.set_library_type(CppLibraryType::Shared);
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc), data);
    }
    {
        let mut data = CppBuildConfigData::new();
        data.set_library_type(CppLibraryType::Static);
        cpp_build_config.add(target::Condition::Env(target::Env::Msvc).negate(), data);
    }
    Ok(FullBuildConfig {
        installation_data,
        cpp_build_config,
        cpp_build_paths,
    })
}

/// Returns library name of the specified module as
/// should be passed to the linker, e.g. `"Qt5Core"`.
pub fn real_lib_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt5{}", sublib_name_capitalized)
}

/// Returns name of the module's include directory, e.g. `"QtCore"`.
pub fn lib_folder_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt{}", sublib_name_capitalized)
}

/// Returns MacOS framework name of the specified module as
/// should be passed to the linker, e.g. `"QtCore"`.
pub fn framework_name(crate_name: &str) -> String {
    let sublib_name = crate_name.replace("qt_", "");
    let sublib_name_capitalized = sublib_name.to_class_case();
    format!("Qt{}", sublib_name_capitalized)
}

pub fn all_crate_names() -> &'static [&'static str] {
    &[
        "qt_core",
        "qt_gui",
        "qt_widgets",
        "qt_ui_tools",
        "qt_3d_core",
        "qt_3d_render",
        "qt_3d_input",
        "qt_3d_logic",
        "qt_3d_extras",
        "qt_charts",
        "qt_qml",
    ]
}

/// Returns list of modules this module depends on.
pub fn lib_dependencies(crate_name: &str) -> Result<&'static [&'static str]> {
    Ok(match crate_name {
        "qt_core" => &[],
        "qt_gui" => &["qt_core"],
        "qt_widgets" => &["qt_core", "qt_gui"],
        "qt_3d_core" => &["qt_core", "qt_gui"],
        "qt_3d_render" | "qt_3d_input" | "qt_3d_logic" => &["qt_core", "qt_gui", "qt_3d_core"],
        "qt_3d_extras" => &[
            "qt_core",
            "qt_gui",
            "qt_3d_core",
            "qt_3d_render",
            "qt_3d_input",
            "qt_3d_logic",
        ],
        "qt_ui_tools" | "qt_charts" => &["qt_core", "qt_gui", "qt_widgets"],
        // NOTE: qt_qml actually depends on qt_network as well
        "qt_qml" => &["qt_core", "qt_gui"],
        "moqt_core" => &[],
        "moqt_gui" => &["moqt_core"],
        _ => bail!("Unknown crate name: {}", crate_name),
    })
}
<|endoftext|>