<commit_before>use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_ast::ImplPolarity;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Warns about a field in a `Send` struct that is neither `Send` nor `Copy`.
    ///
    /// ### Why is this bad?
    /// Sending the struct to another thread and drops it there will also drop
    /// the field in the new thread. This effectively changes the ownership of
    /// the field type to the new thread and creates a soundness issue by
    /// breaking the non-`Send` invariant.
    ///
    /// ### Known Problems
    /// Data structures that contain raw pointers may cause false positives.
    /// They are sometimes safe to be sent across threads but do not implement
    /// the `Send` trait. This lint has a heuristic to filter out basic cases
    /// such as `Vec<*const T>`, but it's not perfect.
    ///
    /// ### Example
    /// ```rust
    /// use std::sync::Arc;
    ///
    /// // There is no `RC: Send` bound here
    /// unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
    ///
    /// #[derive(Debug, Clone)]
    /// pub struct ArcGuard<RC, T> {
    ///     inner: T,
    ///     // Possibly drops `Arc<RC>` (and in turn `RC`) on a wrong thread
    ///     head: Arc<RC>
    /// }
    /// ```
    pub NON_SEND_FIELD_IN_SEND_TY,
    nursery,
    "there is field that does not implement `Send` in a `Send` struct"
}

declare_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]);

impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        // Checks if we are in `Send` impl item.
        // We start from `Send` impl instead of `check_field_def()` because
        // single `AdtDef` may have multiple `Send` impls due to generic
        // parameters, and the lint is much easier to implement in this way.
        if_chain! {
            if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::send_trait);
            if let ItemKind::Impl(hir_impl) = &item.kind;
            if let Some(trait_ref) = &hir_impl.of_trait;
            if let Some(trait_id) = trait_ref.trait_def_id();
            if send_trait == trait_id;
            if let ImplPolarity::Positive = hir_impl.polarity;
            if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
            if let self_ty = ty_trait_ref.self_ty();
            if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
            then {
                for variant in &adt_def.variants {
                    for field in &variant.fields {
                        let field_ty = field.ty(cx.tcx, impl_trait_substs);

                        if raw_pointer_in_ty_def(cx, field_ty)
                            || implements_trait(cx, field_ty, send_trait, &[])
                            || is_copy(cx, field_ty)
                        {
                            continue;
                        }

                        if let Some(field_hir_id) = field
                            .did
                            .as_local()
                            .map(|local_def_id| cx.tcx.hir().local_def_id_to_hir_id(local_def_id))
                        {
                            if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) {
                                span_lint_hir_and_then(
                                    cx,
                                    NON_SEND_FIELD_IN_SEND_TY,
                                    field_hir_id,
                                    field_span,
                                    "non-`Send` field found in a `Send` struct",
                                    |diag| {
                                        diag.span_note(
                                            item.span,
                                            &format!(
                                                "type `{}` doesn't implement `Send` when `{}` is `Send`",
                                                field_ty, self_ty
                                            ),
                                        );
                                        if is_ty_param(field_ty) {
                                            diag.help(&format!("add `{}: Send` bound", field_ty));
                                        }
                                    },
                                );
                            }
                        }
                    }
                }
            }
        }
    }
}

/// Returns `true` if the type itself is a raw pointer or has a raw pointer as a
/// generic parameter, e.g., `Vec<*const u8>`.
/// Note that it does not look into enum variants or struct fields.
fn raw_pointer_in_ty_def<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
    for ty_node in target_ty.walk(cx.tcx) {
        if_chain! {
            if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
            if let ty::RawPtr(_) = inner_ty.kind();
            then {
                return true;
            }
        }
    }

    false
}

/// Returns `true` if the type is a type parameter such as `T`.
fn is_ty_param(target_ty: Ty<'_>) -> bool {
    matches!(target_ty.kind(), ty::Param(_))
}
<commit_msg>Update documentation<commit_after>use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_ast::ImplPolarity;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Warns about a field in a `Send` struct that is neither `Send` nor `Copy`.
    ///
    /// ### Why is this bad?
    /// Sending the struct to another thread will transfer the ownership to
    /// the new thread by dropping in the current thread during the transfer.
    /// This causes soundness issues for non-`Send` fields, as they are also
    /// dropped and might not be set up to handle this.
    ///
    /// See:
    /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
    /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
    ///
    /// ### Known Problems
    /// Data structures that contain raw pointers may cause false positives.
    /// They are sometimes safe to be sent across threads but do not implement
    /// the `Send` trait. This lint has a heuristic to filter out basic cases
    /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an
    /// issue if you have a suggestion on how this heuristic can be improved.
    ///
    /// ### Example
    /// ```rust,ignore
    /// struct ExampleStruct<T> {
    ///     rc_is_not_send: Rc<String>,
    ///     unbounded_generic_field: T,
    /// }
    ///
    /// // This impl is unsound because it allows sending `!Send` types through `ExampleStruct`
    /// unsafe impl<T> Send for ExampleStruct<T> {}
    /// ```
    /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
    /// and specify correct bounds on generic type parameters (`T: Send`).
    pub NON_SEND_FIELD_IN_SEND_TY,
    nursery,
    "there is field that does not implement `Send` in a `Send` struct"
}

declare_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]);

impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        // Checks if we are in `Send` impl item.
        // We start from `Send` impl instead of `check_field_def()` because
        // single `AdtDef` may have multiple `Send` impls due to generic
        // parameters, and the lint is much easier to implement in this way.
        if_chain! {
            if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::send_trait);
            if let ItemKind::Impl(hir_impl) = &item.kind;
            if let Some(trait_ref) = &hir_impl.of_trait;
            if let Some(trait_id) = trait_ref.trait_def_id();
            if send_trait == trait_id;
            if let ImplPolarity::Positive = hir_impl.polarity;
            if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
            if let self_ty = ty_trait_ref.self_ty();
            if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
            then {
                for variant in &adt_def.variants {
                    for field in &variant.fields {
                        let field_ty = field.ty(cx.tcx, impl_trait_substs);

                        if raw_pointer_in_ty_def(cx, field_ty)
                            || implements_trait(cx, field_ty, send_trait, &[])
                            || is_copy(cx, field_ty)
                        {
                            continue;
                        }

                        if let Some(field_hir_id) = field
                            .did
                            .as_local()
                            .map(|local_def_id| cx.tcx.hir().local_def_id_to_hir_id(local_def_id))
                        {
                            if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) {
                                span_lint_hir_and_then(
                                    cx,
                                    NON_SEND_FIELD_IN_SEND_TY,
                                    field_hir_id,
                                    field_span,
                                    "non-`Send` field found in a `Send` struct",
                                    |diag| {
                                        diag.span_note(
                                            item.span,
                                            &format!(
                                                "type `{}` doesn't implement `Send` when `{}` is `Send`",
                                                field_ty, self_ty
                                            ),
                                        );
                                        if is_ty_param(field_ty) {
                                            diag.help(&format!("add `{}: Send` bound", field_ty));
                                        }
                                    },
                                );
                            }
                        }
                    }
                }
            }
        }
    }
}

/// Returns `true` if the type itself is a raw pointer or has a raw pointer as a
/// generic parameter, e.g., `Vec<*const u8>`.
/// Note that it does not look into enum variants or struct fields.
fn raw_pointer_in_ty_def<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
    for ty_node in target_ty.walk(cx.tcx) {
        if_chain! {
            if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
            if let ty::RawPtr(_) = inner_ty.kind();
            then {
                return true;
            }
        }
    }

    false
}

/// Returns `true` if the type is a type parameter such as `T`.
fn is_ty_param(target_ty: Ty<'_>) -> bool {
    matches!(target_ty.kind(), ty::Param(_))
}
<|endoftext|>


<commit_before>use cocoa::base::id;
use cocoa::foundation::NSUInteger;
use libc::c_void;
use {MTLClearColor, MTLOrigin, MTLRegion, MTLSize};

#[link(name = "Metal", kind = "framework")]
extern {
    pub fn MTLCopyAllDevices() -> id;
    pub fn MTLCreateSystemDefaultDevice() -> id;
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLOriginMake(x: NSUInteger, y: NSUInteger, z: NSUInteger) -> MTLOrigin {
    MTLOrigin { x: x, y: y, z: z }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake1D(x: NSUInteger, width: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, 0, 0),
        size: MTLSizeMake(width, 1, 1)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake2D(x: NSUInteger, y: NSUInteger, width: NSUInteger, height: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, y, 0),
        size: MTLSizeMake(width, height, 1)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake3D(x: NSUInteger,
                                  y: NSUInteger,
                                  z: NSUInteger,
                                  width: NSUInteger,
                                  height: NSUInteger,
                                  depth: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, y, z),
        size: MTLSizeMake(width, height, depth)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLSizeMake(width: NSUInteger, height: NSUInteger, depth: NSUInteger) -> MTLSize {
    MTLSize {
        width: width,
        height: height,
        depth: depth
    }
}

/// Returns a value used to clear a color attachment (in effect, when the loadAction property of
/// MTLRenderPassAttachmentDescriptor is MTLLoadActionClear).
///
/// # Arguments
///
/// * `red` - The red color channel clearing value.
/// * `green` - The green color channel clearing value.
/// * `blue` - The blue color channel clearing value.
/// * `alpha` - The alpha color channel clearing value.
///
/// # Return Value
///
/// A value for clearing a color attachment.
#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLClearColorMake(red: f64, green: f64, blue: f64, alpha: f64) -> MTLClearColor {
    MTLClearColor {
        red: red,
        green: green,
        blue: blue,
        alpha: alpha
    }
}
<commit_msg>Remove unused import<commit_after>use cocoa::base::id;
use cocoa::foundation::NSUInteger;
use {MTLClearColor, MTLOrigin, MTLRegion, MTLSize};

#[link(name = "Metal", kind = "framework")]
extern {
    pub fn MTLCopyAllDevices() -> id;
    pub fn MTLCreateSystemDefaultDevice() -> id;
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLOriginMake(x: NSUInteger, y: NSUInteger, z: NSUInteger) -> MTLOrigin {
    MTLOrigin { x: x, y: y, z: z }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake1D(x: NSUInteger, width: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, 0, 0),
        size: MTLSizeMake(width, 1, 1)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake2D(x: NSUInteger, y: NSUInteger, width: NSUInteger, height: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, y, 0),
        size: MTLSizeMake(width, height, 1)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLRegionMake3D(x: NSUInteger,
                                  y: NSUInteger,
                                  z: NSUInteger,
                                  width: NSUInteger,
                                  height: NSUInteger,
                                  depth: NSUInteger) -> MTLRegion {
    MTLRegion {
        origin: MTLOriginMake(x, y, z),
        size: MTLSizeMake(width, height, depth)
    }
}

#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLSizeMake(width: NSUInteger, height: NSUInteger, depth: NSUInteger) -> MTLSize {
    MTLSize {
        width: width,
        height: height,
        depth: depth
    }
}

/// Returns a value used to clear a color attachment (in effect, when the loadAction property of
/// MTLRenderPassAttachmentDescriptor is MTLLoadActionClear).
///
/// # Arguments
///
/// * `red` - The red color channel clearing value.
/// * `green` - The green color channel clearing value.
/// * `blue` - The blue color channel clearing value.
/// * `alpha` - The alpha color channel clearing value.
///
/// # Return Value
///
/// A value for clearing a color attachment.
#[no_mangle]
#[inline(always)]
pub extern "C" fn MTLClearColorMake(red: f64, green: f64, blue: f64, alpha: f64) -> MTLClearColor {
    MTLClearColor {
        red: red,
        green: green,
        blue: blue,
        alpha: alpha
    }
}
<|endoftext|>


<commit_before>//! Selection sort algorithm.
//!
//! The `selection` module contains the simple sorting algorithm "Selection Sort".
//!
//! Source: https://en.wikipedia.org/wiki/Selection_sort

use std::fmt::Debug;

/// Simple sort: selection sort.
///
/// # Details
///
/// Selection sort is one of the simplest sorting algorithm. The list to sort is split into two
/// sections; the first with the sorted element (starting empty) and the second with the remaining
/// element (starting full). At every iteration, the smallest element in the remaining list is
/// found and placed at the end of the sorted list.
///
/// This function takes an immutable reference vector of any type and returns a vector of `usize`
/// of the same length containing the indices of the initial vector sorted.
///
/// # Scaling
///
/// Selection sort in scale as O(N^2) since for all elements to sort, the algorithm will compare it
/// with every (remaining) elements.
///
/// Insertion sort is still useful as it is quite simple.
///
/// # Optimizations
///
/// None
///
/// # Notes
///
/// The type T of the vector elements to sort _must_ implement the `PartialOrd` trait so the
/// compiler knows how to compare the elements and sort them.
///
/// # Examples
///
/// ```
/// let mut data: Vec<i32> = vec![4, 2, 3, 1, 5];
/// sorting::simplesorts::selection::sort(&mut data);
/// assert_eq!(vec![1, 2, 3, 4, 5], data);
/// ```
///
pub fn sort<T: Ord + Debug>(array: &mut Vec<T>) {
    println!("input array:  {:?}", array);

    if !array.is_empty() {
        // Let's loop over the input array, skipping last value (important as we want to get a
        // slice of the following values).
        let n = array.len();
        for i in 0..n-1 {
            println!("    i: {:?}", i);

            // Find index of the minimum value in the slice of values following the i^th element.
            let j = {
                let int_slice = &array[i+1..];
                println!("        array:        {:?}", array);
                println!("        int_slice:    {:?}", int_slice);
                // k now holds the index (of "array") of the minimum element of "int_slice", or the
                // value "i" of the current element of "array" (which should be the less than
                // all elements of "int_slice").
                // "k" is a tuple containing the smallest element of "int_slice" and its index.
                //     k.0: Index of "int_slice" of its smallest value.
                //     k.1: Smallest value of "int_slice".
                let k = int_slice.iter().enumerate().fold(
                    (0, int_slice.first().unwrap()), |acc, item| {
                        if acc.1 > item.1 { item } else { acc }
                });

                // Return from the block the smallest element between the one at index "i" of
                // the "array" and the smallest one from the "int_slice" array.
                // NOTE: Since "k" worked on the subarray "int_slice", it's first element is the
                //       i^th element of "array". If "k" is actually smaller than "array[i]", we
                //       need to adapt the index as stored in "k.0".
                if k.1 < &array[i] { i + k.0 + 1 } else { i }
            };
            println!("            j: {:?}   array[i]={:?}   array[j]={:?}", j, array[i], array[j]);

            array.swap(i, j);
            println!("        new array:    {:?}", array);
        }
    }

    println!("output array: {:?}", array);
}
<commit_msg>Bug fix: Let T implement PartialOrd instead of Ord to be ok on NaN.<commit_after>//! Selection sort algorithm.
//!
//! The `selection` module contains the simple sorting algorithm "Selection Sort".
//!
//! Source: https://en.wikipedia.org/wiki/Selection_sort

use std::fmt::Debug;

/// Simple sort: selection sort.
///
/// # Details
///
/// Selection sort is one of the simplest sorting algorithm. The list to sort is split into two
/// sections; the first with the sorted element (starting empty) and the second with the remaining
/// element (starting full). At every iteration, the smallest element in the remaining list is
/// found and placed at the end of the sorted list.
///
/// This function takes an immutable reference vector of any type and returns a vector of `usize`
/// of the same length containing the indices of the initial vector sorted.
///
/// # Scaling
///
/// Selection sort in scale as O(N^2) since for all elements to sort, the algorithm will compare it
/// with every (remaining) elements.
///
/// Insertion sort is still useful as it is quite simple.
///
/// # Optimizations
///
/// None
///
/// # Notes
///
/// The type T of the vector elements to sort _must_ implement the `PartialOrd` trait so the
/// compiler knows how to compare the elements and sort them.
///
/// # Examples
///
/// ```
/// let mut data: Vec<i32> = vec![4, 2, 3, 1, 5];
/// sorting::simplesorts::selection::sort(&mut data);
/// assert_eq!(vec![1, 2, 3, 4, 5], data);
/// ```
///
pub fn sort<T: PartialOrd + Debug>(array: &mut Vec<T>) {
    println!("input array:  {:?}", array);

    if !array.is_empty() {
        // Let's loop over the input array, skipping last value (important as we want to get a
        // slice of the following values).
        let n = array.len();
        for i in 0..n-1 {
            println!("    i: {:?}", i);

            // Find index of the minimum value in the slice of values following the i^th element.
            let j = {
                let int_slice = &array[i+1..];
                println!("        array:        {:?}", array);
                println!("        int_slice:    {:?}", int_slice);
                // k now holds the index (of "array") of the minimum element of "int_slice", or the
                // value "i" of the current element of "array" (which should be the less than
                // all elements of "int_slice").
                // "k" is a tuple containing the smallest element of "int_slice" and its index.
                //     k.0: Index of "int_slice" of its smallest value.
                //     k.1: Smallest value of "int_slice".
                let k = int_slice.iter().enumerate().fold(
                    (0, int_slice.first().unwrap()), |acc, item| {
                        if acc.1 > item.1 { item } else { acc }
                });

                // Return from the block the smallest element between the one at index "i" of
                // the "array" and the smallest one from the "int_slice" array.
                // NOTE: Since "k" worked on the subarray "int_slice", it's first element is the
                //       i^th element of "array". If "k" is actually smaller than "array[i]", we
                //       need to adapt the index as stored in "k.0".
                if k.1 < &array[i] { i + k.0 + 1 } else { i }
            };
            println!("            j: {:?}   array[i]={:?}   array[j]={:?}", j, array[i], array[j]);

            array.swap(i, j);
            println!("        new array:    {:?}", array);
        }
    }

    println!("output array: {:?}", array);
}
<|endoftext|>


<commit_before>
native "c-stack-cdecl" mod libc = "" {
    fn read(fd: int, buf: *u8, count: uint) -> int;
    fn write(fd: int, buf: *u8, count: uint) -> int;
    fn fread(buf: *u8, size: uint, n: uint, f: libc::FILE) -> uint;
    fn fwrite(buf: *u8, size: uint, n: uint, f: libc::FILE) -> uint;
    fn open(s: str::sbuf, flags: int, mode: uint) -> int = "_open";
    fn close(fd: int) -> int = "_close";
    type FILE;
    fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE;
    fn _fdopen(fd: int, mode: str::sbuf) -> FILE;
    fn fclose(f: FILE);
    fn fgetc(f: FILE) -> int;
    fn ungetc(c: int, f: FILE);
    fn feof(f: FILE) -> int;
    fn fseek(f: FILE, offset: int, whence: int) -> int;
    fn ftell(f: FILE) -> int;
    fn _pipe(fds: *mutable int, size: uint, mode: int) -> int;
}

mod libc_constants {
    fn O_RDONLY() -> int { ret 0; }
    fn O_WRONLY() -> int { ret 1; }
    fn O_RDWR() -> int { ret 2; }
    fn O_APPEND() -> int { ret 8; }
    fn O_CREAT() -> int { ret 256; }
    fn O_EXCL() -> int { ret 1024; }
    fn O_TRUNC() -> int { ret 512; }
    fn O_TEXT() -> int { ret 16384; }
    fn O_BINARY() -> int { ret 32768; }
    fn O_NOINHERIT() -> int { ret 128; }
    fn S_IRUSR() -> uint {
        ret 256u; // really _S_IREAD  in win32

    }
    fn S_IWUSR() -> uint {
        ret 128u; // really _S_IWRITE in win32

    }
}

type DWORD = u32;
type HMODULE = uint;
type LPTSTR = str::sbuf;

native "x86stdcall" mod kernel32 {
    fn GetEnvironmentVariableA(n: str::sbuf, v: str::sbuf, nsize: uint) ->
       uint;
    fn SetEnvironmentVariableA(n: str::sbuf, v: str::sbuf) -> int;
    fn GetModuleFileNameA(hModule: HMODULE,
                          lpFilename: LPTSTR,
                          nSize: DWORD) -> DWORD;
}

fn exec_suffix() -> str { ret ".exe"; }

fn target_os() -> str { ret "win32"; }

fn dylib_filename(base: str) -> str { ret base + ".dll"; }

fn pipe() -> {in: int, out: int} {
    // Windows pipes work subtly differently than unix pipes, and their
    // inheritance has to be handled in a different way that I don't fully
    // understand. Here we explicitly make the pipe non-inheritable,
    // which means to pass it to a subprocess they need to be duplicated
    // first, as in rust_run_program.
    let fds = {mutable in: 0, mutable out: 0};
    let res =
        os::libc::_pipe(ptr::mut_addr_of(fds.in), 1024u,
                        libc_constants::O_BINARY() |
                            libc_constants::O_NOINHERIT());
    assert (res == 0);
    assert (fds.in != -1 && fds.in != 0);
    assert (fds.out != -1 && fds.in != 0);
    ret {in: fds.in, out: fds.out};
}

fn fd_FILE(fd: int) -> libc::FILE {
    ret str::as_buf("r", {|modebuf| libc::_fdopen(fd, modebuf) });
}

fn close(fd: int) -> int {
    libc::close(fd)
}

fn fclose(file: libc::FILE) {
    libc::fclose(file)
}

native "c-stack-cdecl" mod rustrt {
    fn rust_process_wait(handle: int) -> int;
    fn rust_getcwd() -> str;
}

fn waitpid(pid: int) -> int { ret rustrt::rust_process_wait(pid); }

fn getcwd() -> str { ret rustrt::rust_getcwd(); }

fn get_exe_path() -> option::t<fs::path> {
    // FIXME: This doesn't handle the case where the buffer is too small
    let bufsize = 1023u;
    let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize));
    ret str::as_buf(path, { |path_buf|
        if kernel32::GetModuleFileNameA(0u, path_buf,
                                        bufsize as u32) != 0u32 {
            option::some(fs::dirname(path) + fs::path_sep())
        } else {
            option::none
        }
    });
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Convert last use of "x86stdcall" ABI to "c-stack-stdcall"<commit_after>
native "c-stack-cdecl" mod libc = "" {
    fn read(fd: int, buf: *u8, count: uint) -> int;
    fn write(fd: int, buf: *u8, count: uint) -> int;
    fn fread(buf: *u8, size: uint, n: uint, f: libc::FILE) -> uint;
    fn fwrite(buf: *u8, size: uint, n: uint, f: libc::FILE) -> uint;
    fn open(s: str::sbuf, flags: int, mode: uint) -> int = "_open";
    fn close(fd: int) -> int = "_close";
    type FILE;
    fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE;
    fn _fdopen(fd: int, mode: str::sbuf) -> FILE;
    fn fclose(f: FILE);
    fn fgetc(f: FILE) -> int;
    fn ungetc(c: int, f: FILE);
    fn feof(f: FILE) -> int;
    fn fseek(f: FILE, offset: int, whence: int) -> int;
    fn ftell(f: FILE) -> int;
    fn _pipe(fds: *mutable int, size: uint, mode: int) -> int;
}

mod libc_constants {
    fn O_RDONLY() -> int { ret 0; }
    fn O_WRONLY() -> int { ret 1; }
    fn O_RDWR() -> int { ret 2; }
    fn O_APPEND() -> int { ret 8; }
    fn O_CREAT() -> int { ret 256; }
    fn O_EXCL() -> int { ret 1024; }
    fn O_TRUNC() -> int { ret 512; }
    fn O_TEXT() -> int { ret 16384; }
    fn O_BINARY() -> int { ret 32768; }
    fn O_NOINHERIT() -> int { ret 128; }
    fn S_IRUSR() -> uint {
        ret 256u; // really _S_IREAD  in win32

    }
    fn S_IWUSR() -> uint {
        ret 128u; // really _S_IWRITE in win32

    }
}

type DWORD = u32;
type HMODULE = uint;
type LPTSTR = str::sbuf;

native "c-stack-stdcall" mod kernel32 {
    fn GetEnvironmentVariableA(n: str::sbuf, v: str::sbuf, nsize: uint) ->
       uint;
    fn SetEnvironmentVariableA(n: str::sbuf, v: str::sbuf) -> int;
    fn GetModuleFileNameA(hModule: HMODULE,
                          lpFilename: LPTSTR,
                          nSize: DWORD) -> DWORD;
}

fn exec_suffix() -> str { ret ".exe"; }

fn target_os() -> str { ret "win32"; }

fn dylib_filename(base: str) -> str { ret base + ".dll"; }

fn pipe() -> {in: int, out: int} {
    // Windows pipes work subtly differently than unix pipes, and their
    // inheritance has to be handled in a different way that I don't fully
    // understand. Here we explicitly make the pipe non-inheritable,
    // which means to pass it to a subprocess they need to be duplicated
    // first, as in rust_run_program.
    let fds = {mutable in: 0, mutable out: 0};
    let res =
        os::libc::_pipe(ptr::mut_addr_of(fds.in), 1024u,
                        libc_constants::O_BINARY() |
                            libc_constants::O_NOINHERIT());
    assert (res == 0);
    assert (fds.in != -1 && fds.in != 0);
    assert (fds.out != -1 && fds.in != 0);
    ret {in: fds.in, out: fds.out};
}

fn fd_FILE(fd: int) -> libc::FILE {
    ret str::as_buf("r", {|modebuf| libc::_fdopen(fd, modebuf) });
}

fn close(fd: int) -> int {
    libc::close(fd)
}

fn fclose(file: libc::FILE) {
    libc::fclose(file)
}

native "c-stack-cdecl" mod rustrt {
    fn rust_process_wait(handle: int) -> int;
    fn rust_getcwd() -> str;
}

fn waitpid(pid: int) -> int { ret rustrt::rust_process_wait(pid); }

fn getcwd() -> str { ret rustrt::rust_getcwd(); }

fn get_exe_path() -> option::t<fs::path> {
    // FIXME: This doesn't handle the case where the buffer is too small
    let bufsize = 1023u;
    let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize));
    ret str::as_buf(path, { |path_buf|
        if kernel32::GetModuleFileNameA(0u, path_buf,
                                        bufsize as u32) != 0u32 {
            option::some(fs::dirname(path) + fs::path_sep())
        } else {
            option::none
        }
    });
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>//! Utility functions for Header implementations.

use language_tags::LanguageTag;
use std::str;
use std::str::FromStr;
use std::fmt::{self, Display};
use url::percent_encoding;

use header::shared::Charset;

/// Reads a single raw string when parsing a header.
pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<T> {
    if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_raw_str(& unsafe { raw.get_unchecked(0) })
}

/// Reads a raw string into a value.
pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> {
    let s = try!(str::from_utf8(raw));
    T::from_str(s).or(Err(::Error::Header))
}

/// Reads a comma-delimited raw header into a Vec.
#[inline]
pub fn from_comma_delimited<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<Vec<T>> {
    if raw.len() != 1 {
        return Err(::Error::Header);
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..])
}

/// Reads a comma-delimited raw string into a Vec.
pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> ::Result<Vec<T>> {
    let s = try!(str::from_utf8(raw));
    Ok(s.split(',')
        .filter_map(|x| match x.trim() {
            "" => None,
            y => Some(y)
        })
        .filter_map(|x| x.parse().ok())
        .collect())
}

/// Format an array into a comma-delimited string.
pub fn fmt_comma_delimited<T: Display>(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result {
    for (i, part) in parts.iter().enumerate() {
        if i != 0 {
            try!(f.write_str(", "));
        }
        try!(Display::fmt(part, f));
    }
    Ok(())
}

/// An extended header parameter value (i.e., tagged with a character set and optionally,
/// a language), as defined in [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
pub struct ExtendedValue {
    pub charset: Charset,
    pub language_tag: Option<LanguageTag>,
    pub value: Vec<u8>,
}

/// Parses extended header parameter values (`ext-value`), as defined in
/// [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
///
/// Extended values are denoted by parameter names that end with `*`.
///
/// ## ABNF
/// ```plain
/// ext-value     = charset  "'" [ language ] "'" value-chars
///               ; like RFC 2231's <extended-initial-value>
///               ; (see [RFC2231], Section 7)
///
/// charset       = "UTF-8" / "ISO-8859-1" / mime-charset
///
/// mime-charset  = 1*mime-charsetc
/// mime-charsetc = ALPHA / DIGIT
///               / "!" / "#" / "$" / "%" / "&"
///               / "+" / "-" / "^" / "_" / "`"
///               / "{" / "}" / "~"
///               ; as <mime-charset> in Section 2.3 of [RFC2978]
///               ; except that the single quote is not included
///               ; SHOULD be registered in the IANA charset registry
///
/// language      = <Language-Tag, defined in [RFC5646], Section 2.1>
///
/// value-chars   = *( pct-encoded / attr-char )
///
/// pct-encoded   = "%" HEXDIG HEXDIG
///               ; see [RFC3986], Section 2.1
///
/// attr-char     = ALPHA / DIGIT
///               / "!" / "#" / "$" / "&" / "+" / "-" / "."
///               / "^" / "_" / "`" / "|" / "~"
///               ; token except ( "*" / "'" / "%" )
/// ```
pub fn parse_extended_value(val: &str) -> ::Result<ExtendedValue> {

    // Break into three pieces separated by the single-quote character
    let mut parts = val.splitn(3,'\'');

    // Interpret the first piece as a Charset
    let charset: Charset = match parts.next() {
        None => return Err(::Error::Header),
        Some(n) => try!(FromStr::from_str(n)),
    };

    // Interpret the second piece as a language tag
    let lang: Option<LanguageTag> = match parts.next() {
        None => return Err(::Error::Header),
        Some("") => None,
        Some(s) => match s.parse() {
            Ok(lt) => Some(lt),
            Err(_) => return Err(::Error::Header),
        }
    };

    // Interpret the third piece as a sequence of value characters
    let value: Vec<u8> = match parts.next() {
        None => return Err(::Error::Header),
        Some(v) => percent_encoding::percent_decode(v.as_bytes()),
    };

    Ok(ExtendedValue {
        charset: charset,
        language_tag: lang,
        value: value,
    })
}

#[cfg(test)]
mod tests {
    use header::shared::Charset;
    use super::parse_extended_value;

    #[test]
    fn test_parse_extended_value_with_encoding_and_language_tag() {
        let expected_language_tag = langtag!(en);
        // RFC 5987, Section 3.2.2
        // Extended notation, using the Unicode character U+00A3 (POUND SIGN)
        let result = parse_extended_value("iso-8859-1'en'%A3%20rates");
        assert!(result.is_ok());
        let extended_value = result.unwrap();
        assert_eq!(Charset::Iso_8859_1, extended_value.charset);
        assert!(extended_value.language_tag.is_some());
        assert_eq!(expected_language_tag, extended_value.language_tag.unwrap());
        assert_eq!(vec![163, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
    }

    #[test]
    fn test_parse_extended_value_with_encoding() {
        // RFC 5987, Section 3.2.2
        // Extended notation, using the Unicode characters U+00A3 (POUND SIGN)
        // and U+20AC (EURO SIGN)
        let result = parse_extended_value("UTF-8''%c2%a3%20and%20%e2%82%ac%20rates");
        assert!(result.is_ok());
        let extended_value = result.unwrap();
        assert_eq!(Charset::Ext("UTF-8".to_string()), extended_value.charset);
        assert!(extended_value.language_tag.is_none());
        assert_eq!(vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
    }

    #[test]
    fn test_parse_extended_value_missing_language_tag_and_encoding() {
        // From: https://greenbytes.de/tech/tc2231/#attwithfn2231quot2
        let result = parse_extended_value("foo%20bar.html");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_extended_value_partially_formatted() {
        let result = parse_extended_value("UTF-8'missing third part");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_extended_value_partially_formatted_blank() {
        let result = parse_extended_value("blank second part'");
        assert!(result.is_err());
    }
}
<commit_msg>feat(headers): allow ExtendedValue structs to be formatted and used as struct members<commit_after>//! Utility functions for Header implementations.

use language_tags::LanguageTag;
use std::str;
use std::str::FromStr;
use std::fmt::{self, Display};
use url::percent_encoding;

use header::shared::Charset;

/// Reads a single raw string when parsing a header.
pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<T> {
    if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_raw_str(& unsafe { raw.get_unchecked(0) })
}

/// Reads a raw string into a value.
pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> {
    let s = try!(str::from_utf8(raw));
    T::from_str(s).or(Err(::Error::Header))
}

/// Reads a comma-delimited raw header into a Vec.
#[inline]
pub fn from_comma_delimited<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<Vec<T>> {
    if raw.len() != 1 {
        return Err(::Error::Header);
    }
    // we JUST checked that raw.len() == 1, so raw[0] WILL exist.
    from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..])
}

/// Reads a comma-delimited raw string into a Vec.
pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> ::Result<Vec<T>> {
    let s = try!(str::from_utf8(raw));
    Ok(s.split(',')
        .filter_map(|x| match x.trim() {
            "" => None,
            y => Some(y)
        })
        .filter_map(|x| x.parse().ok())
        .collect())
}

/// Format an array into a comma-delimited string.
pub fn fmt_comma_delimited<T: Display>(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result {
    for (i, part) in parts.iter().enumerate() {
        if i != 0 {
            try!(f.write_str(", "));
        }
        try!(Display::fmt(part, f));
    }
    Ok(())
}

/// An extended header parameter value (i.e., tagged with a character set and optionally,
/// a language), as defined in [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
#[derive(Clone, Debug, PartialEq)]
pub struct ExtendedValue {
    pub charset: Charset,
    pub language_tag: Option<LanguageTag>,
    pub value: Vec<u8>,
}

/// Parses extended header parameter values (`ext-value`), as defined in
/// [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
///
/// Extended values are denoted by parameter names that end with `*`.
///
/// ## ABNF
/// ```plain
/// ext-value     = charset  "'" [ language ] "'" value-chars
///               ; like RFC 2231's <extended-initial-value>
///               ; (see [RFC2231], Section 7)
///
/// charset       = "UTF-8" / "ISO-8859-1" / mime-charset
///
/// mime-charset  = 1*mime-charsetc
/// mime-charsetc = ALPHA / DIGIT
///               / "!" / "#" / "$" / "%" / "&"
///               / "+" / "-" / "^" / "_" / "`"
///               / "{" / "}" / "~"
///               ; as <mime-charset> in Section 2.3 of [RFC2978]
///               ; except that the single quote is not included
///               ; SHOULD be registered in the IANA charset registry
///
/// language      = <Language-Tag, defined in [RFC5646], Section 2.1>
///
/// value-chars   = *( pct-encoded / attr-char )
///
/// pct-encoded   = "%" HEXDIG HEXDIG
///               ; see [RFC3986], Section 2.1
///
/// attr-char     = ALPHA / DIGIT
///               / "!" / "#" / "$" / "&" / "+" / "-" / "."
///               / "^" / "_" / "`" / "|" / "~"
///               ; token except ( "*" / "'" / "%" )
/// ```
pub fn parse_extended_value(val: &str) -> ::Result<ExtendedValue> {

    // Break into three pieces separated by the single-quote character
    let mut parts = val.splitn(3,'\'');

    // Interpret the first piece as a Charset
    let charset: Charset = match parts.next() {
        None => return Err(::Error::Header),
        Some(n) => try!(FromStr::from_str(n)),
    };

    // Interpret the second piece as a language tag
    let lang: Option<LanguageTag> = match parts.next() {
        None => return Err(::Error::Header),
        Some("") => None,
        Some(s) => match s.parse() {
            Ok(lt) => Some(lt),
            Err(_) => return Err(::Error::Header),
        }
    };

    // Interpret the third piece as a sequence of value characters
    let value: Vec<u8> = match parts.next() {
        None => return Err(::Error::Header),
        Some(v) => percent_encoding::percent_decode(v.as_bytes()),
    };

    Ok(ExtendedValue {
        charset: charset,
        language_tag: lang,
        value: value,
    })
}

impl Display for ExtendedValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let encoded_value =
            percent_encoding::percent_encode(&self.value[..],
                                             percent_encoding::HTTP_VALUE_ENCODE_SET);
        if let Some(ref lang) = self.language_tag {
            write!(f, "{}'{}'{}", self.charset, lang, encoded_value)
        } else {
            write!(f, "{}''{}", self.charset, encoded_value)
        }
    }
}

#[cfg(test)]
mod tests {
    use header::shared::Charset;
    use super::{ExtendedValue, parse_extended_value};

    #[test]
    fn test_parse_extended_value_with_encoding_and_language_tag() {
        let expected_language_tag = langtag!(en);
        // RFC 5987, Section 3.2.2
        // Extended notation, using the Unicode character U+00A3 (POUND SIGN)
        let result = parse_extended_value("iso-8859-1'en'%A3%20rates");
        assert!(result.is_ok());
        let extended_value = result.unwrap();
        assert_eq!(Charset::Iso_8859_1, extended_value.charset);
        assert!(extended_value.language_tag.is_some());
        assert_eq!(expected_language_tag, extended_value.language_tag.unwrap());
        assert_eq!(vec![163, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
    }

    #[test]
    fn test_parse_extended_value_with_encoding() {
        // RFC 5987, Section 3.2.2
        // Extended notation, using the Unicode characters U+00A3 (POUND SIGN)
        // and U+20AC (EURO SIGN)
        let result = parse_extended_value("UTF-8''%c2%a3%20and%20%e2%82%ac%20rates");
        assert!(result.is_ok());
        let extended_value = result.unwrap();
        assert_eq!(Charset::Ext("UTF-8".to_string()), extended_value.charset);
        assert!(extended_value.language_tag.is_none());
        assert_eq!(vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
    }

    #[test]
    fn test_parse_extended_value_missing_language_tag_and_encoding() {
        // From: https://greenbytes.de/tech/tc2231/#attwithfn2231quot2
        let result = parse_extended_value("foo%20bar.html");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_extended_value_partially_formatted() {
        let result = parse_extended_value("UTF-8'missing third part");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_extended_value_partially_formatted_blank() {
        let result = parse_extended_value("blank second part'");
        assert!(result.is_err());
    }

    #[test]
    fn test_fmt_extended_value_with_encoding_and_language_tag() {
        let extended_value = ExtendedValue {
            charset: Charset::Iso_8859_1,
            language_tag: Some("en".parse().expect("Could not parse language tag")),
            value: vec![163, b' ', b'r', b'a', b't', b'e', b's'],
        };
        assert_eq!("ISO-8859-1'en'%A3%20rates", format!("{}", extended_value));
    }

    #[test]
    fn test_fmt_extended_value_with_encoding() {
        let extended_value = ExtendedValue {
            charset: Charset::Ext("UTF-8".to_string()),
            language_tag: None,
            value: vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a',
                        b't', b'e', b's'],
        };
        assert_eq!("UTF-8''%C2%A3%20and%20%E2%82%AC%20rates",
                   format!("{}", extended_value));
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]

use crate::error::IDLError;
use crate::module::Module;
use crate::package::Package;
use crate::pretty_writer::PrettyWriter;
use crate::types::AtomType;
use crate::types::{
    AliasDataType, DataType, DataTypeRef, DataTypeVariant, EnumDataType, FuncDecl, Ident, Named,
    StructDataType,
};
use heck::{CamelCase, SnakeCase};
use std::collections::HashMap;
use std::io::Write;

/// Generator for the Rust backend
pub struct RustGenerator {
    pub defined: HashMap<Ident, String>,
    pub w: PrettyWriter,
}

impl RustGenerator {
    pub fn new(w: Box<dyn Write>) -> Self {
        Self {
            defined: HashMap::new(),
            w: PrettyWriter::new(w),
        }
    }

    pub fn generate_guest(&mut self, package: &Package) -> Result<(), IDLError> {
        for (_ident, module) in package.modules.iter() {
            self.generate_datatypes(module)?;
            self.w.writeln("extern \"C\" {")?;
            self.w.indent();
            for fdecl in module.func_decls() {
                self.guest_function_import(module, &fdecl)?;
            }
            self.w.eob()?;
            self.w.writeln("}")?;
        }
        Ok(())
    }

    pub fn generate_host(&mut self, package: &Package) -> Result<(), IDLError> {
        for (_ident, module) in package.modules.iter() {
            self.generate_datatypes(module)?;
            for fdecl in module.func_decls() {
                self.w.writeln(format!("// UNIMPLEMENTED: {:?}", fdecl))?;
            }
        }
        Ok(())
    }

    fn generate_datatypes(&mut self, module: &Module) -> Result<(), IDLError> {
        for ref dt in module.datatypes() {
            match &dt.entity.variant {
                DataTypeVariant::Struct(s) => self.gen_struct(module, dt, s)?,
                DataTypeVariant::Alias(a) => self.gen_alias(module, dt, a)?,
                DataTypeVariant::Enum(e) => self.gen_enum(module, dt, e)?,
            }
        }
        Ok(())
    }

    fn define_name(&mut self, dt: &Named<DataType>) -> String {
        let typename = dt.name.name.to_camel_case();
        self.defined.insert(dt.id, typename.clone());
        typename
    }

    fn get_defined_typename(&self, data_type_ref: &DataTypeRef) -> &str {
        match data_type_ref {
            DataTypeRef::Defined(id) => self.defined.get(id).expect("definition exists"),
            DataTypeRef::Atom(a) => Self::atom_name(a),
        }
    }

    fn atom_name(atom_type: &AtomType) -> &'static str {
        use AtomType::*;
        match atom_type {
            Bool => "bool",
            U8 => "u8",
            U16 => "u16",
            U32 => "u32",
            U64 => "u64",
            I8 => "i8",
            I16 => "i16",
            I32 => "i32",
            I64 => "i64",
            F32 => "f32",
            F64 => "f64",
        }
    }

    fn gen_alias(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        alias: &AliasDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);
        let pointee_name = self.get_defined_typename(&alias.to);

        self.w
            .writeln(format!("pub type {} = {};", typename, pointee_name))?
            .eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;
            Ok(())
        })?;

        Ok(())
    }

    fn gen_struct(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        struct_: &StructDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);

        self.w
            .writeln("#[repr(C)]")?
            .writeln(format!("pub struct {} {{", typename))?;

        let mut w = self.w.new_block();
        for m in struct_.members.iter() {
            w.writeln(format!(
                "{}: {},",
                m.name.to_snake_case(),
                self.get_defined_typename(&m.type_)
            ))?;
        }

        self.w.writeln("}")?.eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;

            for m in struct_.members.iter() {
                w.writeln(format!(
                    "assert_eq!({}, {{ let base = ::std::ptr::null::<super::{}>(); unsafe {{ (&(*base).{}) as *const _ as usize }} }});",
                    m.offset, typename, m.name,
                ))?;
            }
            Ok(())
        })?;

        Ok(())
    }

    // Enums generate both a specific typedef, and a traditional C-style enum
    // The typedef is required to use a native type which is consistent across all architectures
    fn gen_enum(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        enum_: &EnumDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);

        self.w
            .writeln("#[repr(C)]")?
            .writeln("#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]")?
            .writeln(format!("pub enum {} {{", typename))?;

        let mut w = self.w.new_block();
        for m in enum_.members.iter() {
            w.writeln(format!("{},", m.name.to_camel_case()))?;
        }

        self.w.writeln("}")?.eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;
            Ok(())
        })?;

        Ok(())
    }

    fn guest_function_import(
        &mut self,
        module: &Module,
        func_decl_entry: &Named<FuncDecl>,
    ) -> Result<(), IDLError> {
        let name = func_decl_entry.name.name.to_snake_case();
        let mut args = String::new();
        for a in func_decl_entry.entity.args.iter() {
            args += &format!(
                "{}: {},",
                a.name.to_snake_case(),
                self.get_defined_typename(&a.type_)
            );
        }

        let func_rets = &func_decl_entry.entity.rets;
        let rets = if func_rets.len() == 0 {
            "()".to_owned()
        } else {
            assert_eq!(func_rets.len(), 1);
            self.get_defined_typename(&func_rets[0].type_).to_owned()
        };

        self.w
            .writeln("#[no_mangle]")?
            .writeln(format!("fn {}({}) -> {};", name, args, rets))?;

        Ok(())
    }
}

fn gen_testcase<F>(w: &mut PrettyWriter, name: &str, f: F) -> Result<(), IDLError>
where
    F: FnOnce(&mut PrettyWriter) -> Result<(), IDLError>,
{
    w.writeln("#[cfg(test)]")?;
    w.writeln(format!("mod {} {{", name))?;
    let mut ww = w.new_block();
    ww.writeln("#[test]")?;
    ww.writeln("fn test() {")?;
    let mut www = ww.new_block();
    f(&mut www)?;
    ww.writeln("}")?;
    ww.eob()?;
    w.writeln("}")?;
    w.eob()?;
    Ok(())
}
<commit_msg>lucet-idl: wip host func<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]

use crate::error::IDLError;
use crate::module::Module;
use crate::package::Package;
use crate::pretty_writer::PrettyWriter;
use crate::types::AtomType;
use crate::types::{
    AliasDataType, DataType, DataTypeRef, DataTypeVariant, EnumDataType, FuncDecl, Ident, Named,
    StructDataType,
};
use heck::{CamelCase, SnakeCase};
use std::collections::HashMap;
use std::io::Write;

/// Generator for the Rust backend
pub struct RustGenerator {
    pub defined: HashMap<Ident, String>,
    pub w: PrettyWriter,
}

impl RustGenerator {
    pub fn new(w: Box<dyn Write>) -> Self {
        Self {
            defined: HashMap::new(),
            w: PrettyWriter::new(w),
        }
    }

    pub fn generate_guest(&mut self, package: &Package) -> Result<(), IDLError> {
        for (_ident, module) in package.modules.iter() {
            self.generate_datatypes(module)?;
            self.w.writeln("extern \"C\" {")?;
            self.w.indent();
            for fdecl in module.func_decls() {
                self.guest_function_import(module, &fdecl)?;
            }
            self.w.eob()?;
            self.w.writeln("}")?;
        }
        Ok(())
    }

    pub fn generate_host(&mut self, package: &Package) -> Result<(), IDLError> {
        for (_ident, module) in package.modules.iter() {
            self.w.writeln("use lucet_runtime::lucet_hostcalls;")?;
            self.generate_datatypes(module)?;
            self.w.writeln("lucet_hostcalls! {")?;
            self.w.indent();
            for fdecl in module.func_decls() {
                self.host_function_definition(module, &fdecl)?;
            }
            self.w.eob()?;
            self.w.writeln("}")?;
        }
        Ok(())
    }

    fn generate_datatypes(&mut self, module: &Module) -> Result<(), IDLError> {
        for ref dt in module.datatypes() {
            match &dt.entity.variant {
                DataTypeVariant::Struct(s) => self.gen_struct(module, dt, s)?,
                DataTypeVariant::Alias(a) => self.gen_alias(module, dt, a)?,
                DataTypeVariant::Enum(e) => self.gen_enum(module, dt, e)?,
            }
        }
        Ok(())
    }

    fn define_name(&mut self, dt: &Named<DataType>) -> String {
        let typename = dt.name.name.to_camel_case();
        self.defined.insert(dt.id, typename.clone());
        typename
    }

    fn get_defined_typename(&self, data_type_ref: &DataTypeRef) -> &str {
        match data_type_ref {
            DataTypeRef::Defined(id) => self.defined.get(id).expect("definition exists"),
            DataTypeRef::Atom(a) => Self::atom_name(a),
        }
    }

    fn atom_name(atom_type: &AtomType) -> &'static str {
        use AtomType::*;
        match atom_type {
            Bool => "bool",
            U8 => "u8",
            U16 => "u16",
            U32 => "u32",
            U64 => "u64",
            I8 => "i8",
            I16 => "i16",
            I32 => "i32",
            I64 => "i64",
            F32 => "f32",
            F64 => "f64",
        }
    }

    fn gen_alias(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        alias: &AliasDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);
        let pointee_name = self.get_defined_typename(&alias.to);

        self.w
            .writeln(format!("pub type {} = {};", typename, pointee_name))?
            .eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;
            Ok(())
        })?;

        Ok(())
    }

    fn gen_struct(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        struct_: &StructDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);

        self.w
            .writeln("#[repr(C)]")?
            .writeln(format!("pub struct {} {{", typename))?;

        let mut w = self.w.new_block();
        for m in struct_.members.iter() {
            w.writeln(format!(
                "{}: {},",
                m.name.to_snake_case(),
                self.get_defined_typename(&m.type_)
            ))?;
        }

        self.w.writeln("}")?.eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;

            for m in struct_.members.iter() {
                w.writeln(format!(
                    "assert_eq!({}, {{ let base = ::std::ptr::null::<super::{}>(); unsafe {{ (&(*base).{}) as *const _ as usize }} }});",
                    m.offset, typename, m.name,
                ))?;
            }
            Ok(())
        })?;

        Ok(())
    }

    // Enums generate both a specific typedef, and a traditional C-style enum
    // The typedef is required to use a native type which is consistent across all architectures
    fn gen_enum(
        &mut self,
        module: &Module,
        dt: &Named<DataType>,
        enum_: &EnumDataType,
    ) -> Result<(), IDLError> {
        let typename = self.define_name(dt);

        self.w
            .writeln("#[repr(C)]")?
            .writeln("#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]")?
            .writeln(format!("pub enum {} {{", typename))?;

        let mut w = self.w.new_block();
        for m in enum_.members.iter() {
            w.writeln(format!("{},", m.name.to_camel_case()))?;
        }

        self.w.writeln("}")?.eob()?;

        gen_testcase(&mut self.w, &dt.name.name.to_snake_case(), |w| {
            w.writeln(format!(
                "assert_eq!({}, ::std::mem::size_of::<super::{}>());",
                dt.entity.repr_size, typename
            ))?;
            Ok(())
        })?;

        Ok(())
    }

    fn guest_function_import(
        &mut self,
        module: &Module,
        func_decl_entry: &Named<FuncDecl>,
    ) -> Result<(), IDLError> {
        let name = func_decl_entry.name.name.to_snake_case();
        let mut args = String::new();
        for a in func_decl_entry.entity.args.iter() {
            args += &format!(
                "{}: {},",
                a.name.to_snake_case(),
                self.get_defined_typename(&a.type_)
            );
        }

        let func_rets = &func_decl_entry.entity.rets;
        let rets = if func_rets.len() == 0 {
            "()".to_owned()
        } else {
            assert_eq!(func_rets.len(), 1);
            self.get_defined_typename(&func_rets[0].type_).to_owned()
        };

        self.w
            .writeln("#[no_mangle]")?
            .writeln(format!("fn {}({}) -> {};", name, args, rets))?;

        Ok(())
    }

    fn host_function_definition(
        &mut self,
        module: &Module,
        func_decl_entry: &Named<FuncDecl>,
    ) -> Result<(), IDLError> {
        let name = func_decl_entry.name.name.to_snake_case();
        let mut args = format!("&mut vmctx,");
        for a in func_decl_entry.entity.args.iter() {
            args += &format!(
                "{}: {},",
                a.name.to_snake_case(),
                self.get_defined_typename(&a.type_)
            );
        }

        let func_rets = &func_decl_entry.entity.rets;
        let rets = if func_rets.len() == 0 {
            "()".to_owned()
        } else {
            assert_eq!(func_rets.len(), 1);
            self.get_defined_typename(&func_rets[0].type_).to_owned()
        };

        self.w.writeln("#[no_mangle]")?.writeln(format!(
            "pub unsafe extern \"C\" fn {}({}) -> {} {{",
            name, args, rets
        ))?;

        self.w.indent();
        self.w.writeln("unimplemented!()")?;
        self.w.eob()?;

        self.w.writeln("}")?;

        Ok(())
    }
}

fn gen_testcase<F>(w: &mut PrettyWriter, name: &str, f: F) -> Result<(), IDLError>
where
    F: FnOnce(&mut PrettyWriter) -> Result<(), IDLError>,
{
    w.writeln("#[cfg(test)]")?;
    w.writeln(format!("mod {} {{", name))?;
    let mut ww = w.new_block();
    ww.writeln("#[test]")?;
    ww.writeln("fn test() {")?;
    let mut www = ww.new_block();
    f(&mut www)?;
    ww.writeln("}")?;
    ww.eob()?;
    w.writeln("}")?;
    w.eob()?;
    Ok(())
}
<|endoftext|>


<commit_before>//! Manager that is called when a seat is created or destroyed.
//! Pass a struct that implements this trait to the `Compositor` during
//! initialization.

use libc;

use std::{env, panic};
use std::process::abort;

use super::{KeyboardHandler, KeyboardWrapper, PointerHandler, PointerWrapper, TouchHandler,
            TouchWrapper};
use compositor::{Compositor, COMPOSITOR_PTR};
use types::input::{InputDevice, Keyboard, Pointer, Touch};
use utils::safe_as_cstring;

use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wayland_sys::server::signal::wl_signal_add;
use wlroots_sys::{wlr_input_device, wlr_input_device_type, wlr_keyboard_set_keymap,
                  xkb_context_new, xkb_context_unref, xkb_keymap_new_from_names, xkb_rule_names};
use wlroots_sys::xkb_context_flags::*;
use wlroots_sys::xkb_keymap_compile_flags::*;

/// Different type of inputs that can be acquired.
pub enum Input {
    Keyboard(Box<KeyboardWrapper>),
    Pointer(Box<PointerWrapper>),
    Touch(Box<TouchWrapper>)
}

impl Input {
    pub(crate) unsafe fn input_device(&self) -> *mut wlr_input_device {
        use self::Input::*;
        match *self {
            Keyboard(ref keyboard) => keyboard.input_device().as_ptr(),
            Pointer(ref pointer) => pointer.input_device().as_ptr(),
            Touch(ref touch) => touch.input_device().as_ptr()
        }
    }
}

/// Handles input addition and removal.
pub trait InputManagerHandler {
    /// Callback triggered when an input device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn input_added(&mut self, &mut Compositor, &mut InputDevice) {}

    /// Callback triggered when an input device is removed.
    fn input_removed(&mut self, &mut Compositor, &mut InputDevice) {
        // TODO
    }

    /// Callback triggered when a keyboard device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn keyboard_added(&mut self, &mut Compositor, &mut Keyboard) -> Option<Box<KeyboardHandler>> {
        None
    }

    /// Callback triggered when a pointer device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn pointer_added(&mut self, &mut Compositor, &mut Pointer) -> Option<Box<PointerHandler>> {
        None
    }

    /// Callback triggered when a touch device is added.
    ///
    /// # Panics
    /// Any panic in this functino will cause the process to abort.
    fn touch_added(&mut self, &mut Compositor, &mut Touch) -> Option<Box<TouchHandler>> {
        None
    }
}

wayland_listener!(InputManager, (Vec<Input>, Box<InputManagerHandler>), [
    add_listener => add_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
        let data = data as *mut wlr_input_device;
        let remove_listener = this.remove_listener()  as *mut _ as _;
        let (ref mut inputs, ref mut manager) = this.data;
        use self::wlr_input_device_type::*;
        let mut dev = InputDevice::from_ptr(data);
        let compositor = &mut *COMPOSITOR_PTR;
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
            match dev.dev_type() {
                WLR_INPUT_DEVICE_KEYBOARD => {
                    // Boring setup that we won't make the user do
                    add_keyboard(&mut dev);
                    let mut keyboard_handle = match Keyboard::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a keyboard!", dev);
                            abort()
                        }
                    };
                    keyboard_handle.set_lock(true);
                    if let Some(keyboard_handler) = manager.keyboard_added(compositor,
                                                                           &mut keyboard_handle) {
                        keyboard_handle.set_lock(false);
                        let mut keyboard = KeyboardWrapper::new((keyboard_handle,
                                                                 keyboard_handler));
                        wl_signal_add(&mut (*dev.dev_union().keyboard).events.key as *mut _ as _,
                                    keyboard.key_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Keyboard(keyboard));
                    }
                },
                WLR_INPUT_DEVICE_POINTER => {
                    let mut pointer_handle = match Pointer::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a pointer!", dev);
                            abort()
                        }
                    };
                    pointer_handle.set_lock(true);
                    if let Some(pointer) = manager.pointer_added(compositor, &mut pointer_handle) {
                        pointer_handle.set_lock(false);
                        let mut pointer = PointerWrapper::new((pointer_handle, pointer));
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.motion as *mut _ as _,
                                    pointer.motion_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer)
                                      .events.motion_absolute as *mut _ as _,
                                    pointer.motion_absolute_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.button as *mut _ as _,
                                    pointer.button_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.axis as *mut _ as _,
                                    pointer.axis_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Pointer(pointer))
                    }
                },
                WLR_INPUT_DEVICE_TOUCH => {
                    let mut touch_handle = match Touch::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a touch", dev);
                            abort()
                        }
                    };
                    touch_handle.set_lock(true);
                    if let Some(touch) = manager.touch_added(compositor, &mut touch_handle) {
                        touch_handle.set_lock(false);
                        let mut touch = TouchWrapper::new((touch_handle, touch));
                        wl_signal_add(&mut (*dev.dev_union().touch).events.down as *mut _ as _,
                                      touch.down_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.up as *mut _ as _,
                                      touch.up_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.motion as *mut _ as _,
                                      touch.motion_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.cancel as *mut _ as _,
                                      touch.cancel_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Touch(touch))
                    }
                }
                _ => unimplemented!(),
            }
            manager.input_added(compositor, &mut dev)
        }));
        wl_signal_add(&mut (*dev.as_ptr()).events.destroy as *mut _ as _,
                      remove_listener);
        match res {
            Ok(_) => {},
            // NOTE
            // Either Wayland or wlroots does not handle failure to set up input correctly.
            // Calling wl_display_terminate does not work if input is incorrectly set up.
            //
            // Instead, execution keeps going with an eventual segfault (if lucky).
            //
            // To fix this, we abort the process if there was a panic in input setup.
            Err(_) => abort()
        }
    };
    remove_listener => remove_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
        let data = data as *mut wlr_input_device;
        let (ref mut inputs, ref mut manager) = this.data;
        if COMPOSITOR_PTR.is_null() {
            // We are shutting down, do nothing.
            return;
        }
        let compositor = &mut *COMPOSITOR_PTR;
        manager.input_removed(compositor, &mut InputDevice::from_ptr(data));
        // Remove user output data
        let find_index = inputs.iter()
            .position(|input| input.input_device() == data);
        if let Some(index) = find_index {
            let removed_input = inputs.remove(index);
            match removed_input {
                Input::Keyboard(mut keyboard) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*keyboard.key_listener()).link as *mut _ as _);
                },
                Input::Pointer(mut pointer) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.button_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.motion_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.motion_absolute_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.axis_listener()).link as *mut _ as _);
                },
                Input::Touch(mut touch) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.down_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.up_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.motion_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.cancel_listener()).link as *mut _ as _);
                }
            }
        }
    };
]);

pub(crate) unsafe fn add_keyboard(dev: &mut InputDevice) {
    // Set the XKB settings
    let rules = safe_as_cstring(env::var("XKB_DEFAULT_RULES").unwrap_or("".into()));
    let model = safe_as_cstring(env::var("XKB_DEFAULT_MODEL").unwrap_or("".into()));
    let layout = safe_as_cstring(env::var("XKB_DEFAULT_LAYOUT").unwrap_or("".into()));
    let variant = safe_as_cstring(env::var("XKB_DEFAULT_VARIANT").unwrap_or("".into()));
    let options = safe_as_cstring(env::var("XKB_DEFAULT_OPTIONS").unwrap_or("".into()));
    let rules = xkb_rule_names { rules: rules.into_raw(),
                                 model: model.into_raw(),
                                 layout: layout.into_raw(),
                                 variant: variant.into_raw(),
                                 options: options.into_raw() };
    let context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
    if context.is_null() {
        wlr_log!(L_ERROR, "Failed to create XKB context");
        // NOTE We don't panic here, because we have a C call stack above us
        ::std::process::exit(1)
    }
    let xkb_map = xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
    wlr_keyboard_set_keymap(dev.dev_union().keyboard, xkb_map);
    xkb_context_unref(context);
}
<commit_msg>Debug print xkb, unref the context and set repeat info<commit_after>//! Manager that is called when a seat is created or destroyed.
//! Pass a struct that implements this trait to the `Compositor` during
//! initialization.

use libc;

use std::{env, panic};
use std::process::abort;

use super::{KeyboardHandler, KeyboardWrapper, PointerHandler, PointerWrapper, TouchHandler,
            TouchWrapper};
use compositor::{Compositor, COMPOSITOR_PTR};
use types::input::{InputDevice, Keyboard, Pointer, Touch};
use utils::safe_as_cstring;

use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wayland_sys::server::signal::wl_signal_add;
use wlroots_sys::{wlr_input_device, wlr_input_device_type, wlr_keyboard_set_keymap,
                  wlr_keyboard_set_repeat_info, xkb_context_new, xkb_context_unref,
                  xkb_keymap_new_from_names, xkb_keymap_unref, xkb_rule_names};
use wlroots_sys::xkb_context_flags::*;
use wlroots_sys::xkb_keymap_compile_flags::*;

/// Different type of inputs that can be acquired.
pub enum Input {
    Keyboard(Box<KeyboardWrapper>),
    Pointer(Box<PointerWrapper>),
    Touch(Box<TouchWrapper>)
}

impl Input {
    pub(crate) unsafe fn input_device(&self) -> *mut wlr_input_device {
        use self::Input::*;
        match *self {
            Keyboard(ref keyboard) => keyboard.input_device().as_ptr(),
            Pointer(ref pointer) => pointer.input_device().as_ptr(),
            Touch(ref touch) => touch.input_device().as_ptr()
        }
    }
}

/// Handles input addition and removal.
pub trait InputManagerHandler {
    /// Callback triggered when an input device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn input_added(&mut self, &mut Compositor, &mut InputDevice) {}

    /// Callback triggered when an input device is removed.
    fn input_removed(&mut self, &mut Compositor, &mut InputDevice) {
        // TODO
    }

    /// Callback triggered when a keyboard device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn keyboard_added(&mut self, &mut Compositor, &mut Keyboard) -> Option<Box<KeyboardHandler>> {
        None
    }

    /// Callback triggered when a pointer device is added.
    ///
    /// # Panics
    /// Any panic in this function will cause the process to abort.
    fn pointer_added(&mut self, &mut Compositor, &mut Pointer) -> Option<Box<PointerHandler>> {
        None
    }

    /// Callback triggered when a touch device is added.
    ///
    /// # Panics
    /// Any panic in this functino will cause the process to abort.
    fn touch_added(&mut self, &mut Compositor, &mut Touch) -> Option<Box<TouchHandler>> {
        None
    }
}

wayland_listener!(InputManager, (Vec<Input>, Box<InputManagerHandler>), [
    add_listener => add_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
        let data = data as *mut wlr_input_device;
        let remove_listener = this.remove_listener()  as *mut _ as _;
        let (ref mut inputs, ref mut manager) = this.data;
        use self::wlr_input_device_type::*;
        let mut dev = InputDevice::from_ptr(data);
        let compositor = &mut *COMPOSITOR_PTR;
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
            match dev.dev_type() {
                WLR_INPUT_DEVICE_KEYBOARD => {
                    // Boring setup that we won't make the user do
                    add_keyboard(&mut dev);
                    let mut keyboard_handle = match Keyboard::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a keyboard!", dev);
                            abort()
                        }
                    };
                    keyboard_handle.set_lock(true);
                    if let Some(keyboard_handler) = manager.keyboard_added(compositor,
                                                                           &mut keyboard_handle) {
                        keyboard_handle.set_lock(false);
                        let mut keyboard = KeyboardWrapper::new((keyboard_handle,
                                                                 keyboard_handler));
                        wl_signal_add(&mut (*dev.dev_union().keyboard).events.key as *mut _ as _,
                                    keyboard.key_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Keyboard(keyboard));
                    }
                },
                WLR_INPUT_DEVICE_POINTER => {
                    let mut pointer_handle = match Pointer::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a pointer!", dev);
                            abort()
                        }
                    };
                    pointer_handle.set_lock(true);
                    if let Some(pointer) = manager.pointer_added(compositor, &mut pointer_handle) {
                        pointer_handle.set_lock(false);
                        let mut pointer = PointerWrapper::new((pointer_handle, pointer));
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.motion as *mut _ as _,
                                    pointer.motion_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer)
                                      .events.motion_absolute as *mut _ as _,
                                    pointer.motion_absolute_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.button as *mut _ as _,
                                    pointer.button_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().pointer).events.axis as *mut _ as _,
                                    pointer.axis_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Pointer(pointer))
                    }
                },
                WLR_INPUT_DEVICE_TOUCH => {
                    let mut touch_handle = match Touch::new_from_input_device(data) {
                        Some(dev) => dev,
                        None => {
                            wlr_log!(L_ERROR, "Device {:#?} was not a touch", dev);
                            abort()
                        }
                    };
                    touch_handle.set_lock(true);
                    if let Some(touch) = manager.touch_added(compositor, &mut touch_handle) {
                        touch_handle.set_lock(false);
                        let mut touch = TouchWrapper::new((touch_handle, touch));
                        wl_signal_add(&mut (*dev.dev_union().touch).events.down as *mut _ as _,
                                      touch.down_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.up as *mut _ as _,
                                      touch.up_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.motion as *mut _ as _,
                                      touch.motion_listener() as *mut _ as _);
                        wl_signal_add(&mut (*dev.dev_union().touch).events.cancel as *mut _ as _,
                                      touch.cancel_listener() as *mut _ as _);
                        // Forget until we need to drop it in the destroy callback
                        inputs.push(Input::Touch(touch))
                    }
                }
                _ => unimplemented!(),
            }
            manager.input_added(compositor, &mut dev)
        }));
        wl_signal_add(&mut (*dev.as_ptr()).events.destroy as *mut _ as _,
                      remove_listener);
        match res {
            Ok(_) => {},
            // NOTE
            // Either Wayland or wlroots does not handle failure to set up input correctly.
            // Calling wl_display_terminate does not work if input is incorrectly set up.
            //
            // Instead, execution keeps going with an eventual segfault (if lucky).
            //
            // To fix this, we abort the process if there was a panic in input setup.
            Err(_) => abort()
        }
    };
    remove_listener => remove_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
        let data = data as *mut wlr_input_device;
        let (ref mut inputs, ref mut manager) = this.data;
        if COMPOSITOR_PTR.is_null() {
            // We are shutting down, do nothing.
            return;
        }
        let compositor = &mut *COMPOSITOR_PTR;
        manager.input_removed(compositor, &mut InputDevice::from_ptr(data));
        // Remove user output data
        let find_index = inputs.iter()
            .position(|input| input.input_device() == data);
        if let Some(index) = find_index {
            let removed_input = inputs.remove(index);
            match removed_input {
                Input::Keyboard(mut keyboard) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*keyboard.key_listener()).link as *mut _ as _);
                },
                Input::Pointer(mut pointer) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.button_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.motion_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.motion_absolute_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*pointer.axis_listener()).link as *mut _ as _);
                },
                Input::Touch(mut touch) => {
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.down_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.up_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.motion_listener()).link as *mut _ as _);
                    ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                  wl_list_remove,
                                  &mut (*touch.cancel_listener()).link as *mut _ as _);
                }
            }
        }
    };
]);

pub(crate) unsafe fn add_keyboard(dev: &mut InputDevice) {
    // Set the XKB settings
    let rules = safe_as_cstring(env::var("XKB_DEFAULT_RULES").unwrap_or("".into()));
    let model = safe_as_cstring(env::var("XKB_DEFAULT_MODEL").unwrap_or("".into()));
    let layout = safe_as_cstring(env::var("XKB_DEFAULT_LAYOUT").unwrap_or("".into()));
    let variant = safe_as_cstring(env::var("XKB_DEFAULT_VARIANT").unwrap_or("".into()));
    let options = safe_as_cstring(env::var("XKB_DEFAULT_OPTIONS").unwrap_or("".into()));
    wlr_log!(L_DEBUG, "Using xkb rules: {:?}", rules);
    wlr_log!(L_DEBUG, "Using xkb model: {:?}", model);
    wlr_log!(L_DEBUG, "Using xkb layout: {:?}", layout);
    wlr_log!(L_DEBUG, "Using xkb variant: {:?}", variant);
    wlr_log!(L_DEBUG, "Using xkb options: {:?}", options);
    let rules = xkb_rule_names { rules: rules.into_raw(),
                                 model: model.into_raw(),
                                 layout: layout.into_raw(),
                                 variant: variant.into_raw(),
                                 options: options.into_raw() };
    let context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
    if context.is_null() {
        panic!("Failed to create XKB context");
    }
    let xkb_map = xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
    if xkb_map.is_null() {
        panic!("Could not create xkb map");
    }
    wlr_keyboard_set_keymap(dev.dev_union().keyboard, xkb_map);
    xkb_keymap_unref(xkb_map);
    xkb_context_unref(context);
    wlr_keyboard_set_repeat_info(dev.dev_union().keyboard, 25, 600);
}
<|endoftext|>