<commit_before>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( &self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }

    /// Gets the sype system the handler serves if the handler is type system specific. Returns
    /// None if the handler is type system agnostic.
    fn type_system( &self ) -> Option<ModelTypeSystem> { None }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam { ty: Type, context: TypeContext }

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        use syn;
        let rust_ty = self.rust_ty();
        let itf_ty = match rust_ty {
            syn::Type::Path( path ) =>
                match path.path.segments.last().unwrap().value().arguments {
                    syn::PathArguments::AngleBracketed( ref ab ) =>
                            match ab.args.last().unwrap().value() {
                                syn::GenericArgument::Type( ref t ) => t.clone(),
                                _ => panic!( "ComItf generic argument must be type" ),
                            },
                    _ => panic!( "ComItf type parameter must be angle bracketed" ),
                },
            _ => panic!( "ComItf type parameter must be a type path" ),
        };
        parse_quote!( ::intercom::raw::InterfacePtr< #itf_ty > )
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::intercom::raw::InterfacePtr::new( ::std::ptr::null_mut() ) )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::wrap( #ident.ptr, #ts ) ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::ptr( &#ident.into(), #ts ) )
        }
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.dir {
            Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
            Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let to_intermediate = quote!( ::intercom::BStr::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from_ptr( #ident ).com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from( #ident ).into_ptr() ),
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }

    /// String parameters differ between the type systems.
    fn type_system( &self ) -> Option<ModelTypeSystem> {
        Some( self.context.type_system )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
        "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<commit_msg>Fix clippy issue<commit_after>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }

    /// Gets the sype system the handler serves if the handler is type system specific. Returns
    /// None if the handler is type system agnostic.
    fn type_system( &self ) -> Option<ModelTypeSystem> { None }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam { ty: Type, context: TypeContext }

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        use syn;
        let rust_ty = self.rust_ty();
        let itf_ty = match rust_ty {
            syn::Type::Path( path ) =>
                match path.path.segments.last().unwrap().value().arguments {
                    syn::PathArguments::AngleBracketed( ref ab ) =>
                            match ab.args.last().unwrap().value() {
                                syn::GenericArgument::Type( ref t ) => t.clone(),
                                _ => panic!( "ComItf generic argument must be type" ),
                            },
                    _ => panic!( "ComItf type parameter must be angle bracketed" ),
                },
            _ => panic!( "ComItf type parameter must be a type path" ),
        };
        parse_quote!( ::intercom::raw::InterfacePtr< #itf_ty > )
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::intercom::raw::InterfacePtr::new( ::std::ptr::null_mut() ) )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::wrap( #ident.ptr, #ts ) ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::ptr( &#ident.into(), #ts ) )
        }
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.dir {
            Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
            Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let to_intermediate = quote!( ::intercom::BStr::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from_ptr( #ident ).com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from( #ident ).into_ptr() ),
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }

    /// String parameters differ between the type systems.
    fn type_system( &self ) -> Option<ModelTypeSystem> {
        Some( self.context.type_system )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
        "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<|endoftext|>


<commit_before>#![cfg(windows)]

extern crate libc;
extern crate winapi;
extern crate "kernel32-sys" as kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed");
        }
    );
}

pub struct Screen {
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    conin: HANDLE,
    conout: HANDLE,
}

impl Drop for Screen {
    fn drop(&mut self) {
        win32!(SetConsoleMode(self.conout, self.original_console_mode));
    }
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            // Unlike in Linux, we *could* use the default stdin/stdout file handles to talk
            // directly to the console. However, we need the default stdin handle for the list of
            // choices, and we need to write just the final selection to stdout. Therefore we have
            // to explicitly create new handles to talk to the console. The gory details are
            // available at:
            //
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075%28v=vs.85%29.aspx
            // http://stackoverflow.com/questions/377152/what-does-createfileconin-do
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (cols, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conout, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));

        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        let original_colors = Screen::get_original_colors(conout);
        Screen {
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
            original_console_mode: orig_mode,
            original_colors: original_colors,
            conin: conin,
            conout: conout,
        }
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    pub fn move_cursor_to_bottom(&mut self) {
        let end_line = self.start_line + self.visible_choices;
        self.move_cursor(end_line, 0);
        self.write("\n");
    }

    pub fn blank_screen(&mut self) {
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
        for _ in 0..self.height {
            self.write(&blank_line);
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        let mut copy = s.to_string();
        let len: DWORD = copy.len() as DWORD;
        copy.push('\0');
        let mut bytes_written: DWORD = 0;
        win32!(WriteFile(self.conout, copy.as_ptr() as PVOID, len, &mut bytes_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    // Currently the Windows implementation does not buffer, so this function just performs a
    // blocking read of a single key.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut buf: Vec<u16> = repeat(0u16).take(0x1000).collect();
        let mut chars_read: DWORD = 0;
        win32!(ReadFile(self.conin, buf.as_mut_ptr() as LPVOID, 1, &mut chars_read as LPDWORD, ptr::null_mut()));

        let mut ret = Vec::new();
        for i in 0..chars_read {
            ret.push(Screen::translate_byte(buf[i as usize]));
        }
        ret
    }

    fn translate_byte(byte: u16) -> Key {
        if byte == '\r' as u16 {
            Enter
        } else if byte == 127 {
            Backspace
        } else if byte & 96 == 0 {
            Control(((byte + 96u16) as u8) as char)
        } else {
            Char((byte as u8) as char)
        }
    }

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conin: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conin, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let conout = unsafe { kernel32::GetStdHandle(-11) };
    let (cols, rows) = Screen::winsize(conout).expect("Failed to get window size!");
    // We don't know the window size a priori, but we can at least
    // assert that it is within some kind of sensible range.
    assert!(cols > 40);
    assert!(rows > 40);
    assert!(cols < 1000);
    assert!(rows < 1000);
}
<commit_msg>Fix misleading function parameter name<commit_after>#![cfg(windows)]

extern crate libc;
extern crate winapi;
extern crate "kernel32-sys" as kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed");
        }
    );
}

pub struct Screen {
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    conin: HANDLE,
    conout: HANDLE,
}

impl Drop for Screen {
    fn drop(&mut self) {
        win32!(SetConsoleMode(self.conout, self.original_console_mode));
    }
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            // Unlike in Linux, we *could* use the default stdin/stdout file handles to talk
            // directly to the console. However, we need the default stdin handle for the list of
            // choices, and we need to write just the final selection to stdout. Therefore we have
            // to explicitly create new handles to talk to the console. The gory details are
            // available at:
            //
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075%28v=vs.85%29.aspx
            // http://stackoverflow.com/questions/377152/what-does-createfileconin-do
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (cols, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conout, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));

        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        let original_colors = Screen::get_original_colors(conout);
        Screen {
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
            original_console_mode: orig_mode,
            original_colors: original_colors,
            conin: conin,
            conout: conout,
        }
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    pub fn move_cursor_to_bottom(&mut self) {
        let end_line = self.start_line + self.visible_choices;
        self.move_cursor(end_line, 0);
        self.write("\n");
    }

    pub fn blank_screen(&mut self) {
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
        for _ in 0..self.height {
            self.write(&blank_line);
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        let mut copy = s.to_string();
        let len: DWORD = copy.len() as DWORD;
        copy.push('\0');
        let mut bytes_written: DWORD = 0;
        win32!(WriteFile(self.conout, copy.as_ptr() as PVOID, len, &mut bytes_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    // Currently the Windows implementation does not buffer, so this function just performs a
    // blocking read of a single key.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut buf: Vec<u16> = repeat(0u16).take(0x1000).collect();
        let mut chars_read: DWORD = 0;
        win32!(ReadFile(self.conin, buf.as_mut_ptr() as LPVOID, 1, &mut chars_read as LPDWORD, ptr::null_mut()));

        let mut ret = Vec::new();
        for i in 0..chars_read {
            ret.push(Screen::translate_byte(buf[i as usize]));
        }
        ret
    }

    fn translate_byte(byte: u16) -> Key {
        if byte == '\r' as u16 {
            Enter
        } else if byte == 127 {
            Backspace
        } else if byte & 96 == 0 {
            Control(((byte + 96u16) as u8) as char)
        } else {
            Char((byte as u8) as char)
        }
    }

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conout: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conout, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let conout = unsafe { kernel32::GetStdHandle(-11) };
    let (cols, rows) = Screen::winsize(conout).expect("Failed to get window size!");
    // We don't know the window size a priori, but we can at least
    // assert that it is within some kind of sensible range.
    assert!(cols > 40);
    assert!(rows > 40);
    assert!(cols < 1000);
    assert!(rows < 1000);
}
<|endoftext|>


<commit_before>mod event;
mod input;
use std::mem;
use std::fs::{File, OpenOptions};
use std::io::{self, Write, Read};
use std::os::unix::io::AsRawFd;
use libc::{TCSANOW, TIOCGWINSZ, termios, winsize, c_int, c_ulong};

pub use self::input::*;
pub use self::event::*;

extern {
    fn tcgetattr(filedes: c_int, termptr: *mut termios) -> c_int;
    fn tcsetattr(filedes: c_int, opt: c_int, termptr: *const termios) -> c_int;
    fn cfmakeraw(termptr: *mut termios);
    fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
}

#[derive(Debug)]
pub enum Error {
    TcGetAttr,
    TcSetAttr
}

pub struct Terminal {
    file: File,
    prev_termios: Option<termios>,
    pub max_width: usize,
    pub max_height: usize
}

impl Terminal {
    /// Creates a new Terminal from the provided filename
    pub fn from(filename: &str) -> io::Result<Terminal> {
        let file = OpenOptions::new().write(true).read(true).open(filename)?;
        let fd = file.as_raw_fd();

        let mut terminal = Terminal {
            file: file,
            prev_termios: None,
            max_width: 80,
            max_height: 25
        };

        unsafe {
            let mut ws: winsize = mem::zeroed();
            if ioctl(fd, TIOCGWINSZ, &mut ws) != -1 {
                terminal.max_width = ws.ws_col as usize;
                terminal.max_height = ws.ws_row as usize;
            }
        }

        Ok(terminal)
    }

    pub fn set_raw_mode(&mut self) -> Result<(), Error> {
        let fd = self.file.as_raw_fd();

        unsafe {
            let mut ios: termios = mem::zeroed();

            // get the existing termios
            if tcgetattr(fd, &mut ios) != 0 {
                return Err(Error::TcGetAttr);
            }

            self.prev_termios = Some(ios);

            // enable raw mode
            cfmakeraw(&mut ios);

            // apply the raw mode termios
            if tcsetattr(fd, TCSANOW, &ios) != 0 {
                return Err(Error::TcSetAttr);
            }
        }

        Ok(())
    }

    pub fn reset(&mut self) -> Result<(), Error> {
        if let Some(opts) = self.prev_termios {
            let fd = self.file.as_raw_fd();

            // disable raw mode, by setting the original termios
            unsafe {
                if tcsetattr(fd, TCSANOW, &opts) != 0 {
                    return Err(Error::TcSetAttr);
                }
            }
        }

        Ok(())
    }

    pub fn events(&self) -> io::Result<Events<File>> {
        self.file.try_clone().map(Events::new)
    }
}

impl Read for Terminal {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file.read(buf)
    }
}

impl Write for Terminal {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file.flush()
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.reset().unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    #[test]
    fn terminal() {
        let mut term = Terminal::from("/dev/tty").expect("Unable to open /dev/tty");

        // if all is good, this should _not_ break the terminal, because the
        // Drop trait impl will clean up
        term.set_raw_mode().expect("Unable to enable raw mode");

        write!(term, "").unwrap();
        term.flush().unwrap();
    }
}
<commit_msg>Lean on libc, rather than defining externs ourselves<commit_after>mod event;
mod input;
use std::mem;
use std::fs::{File, OpenOptions};
use std::io::{self, Write, Read};
use std::os::unix::io::AsRawFd;
use libc::{TCSANOW, TIOCGWINSZ, winsize};
use libc::{termios, tcgetattr, tcsetattr};
use libc::{ioctl, cfmakeraw};

pub use self::input::*;
pub use self::event::*;

#[derive(Debug)]
pub enum Error {
    TcGetAttr,
    TcSetAttr
}

pub struct Terminal {
    file: File,
    prev_termios: Option<termios>,
    pub max_width: usize,
    pub max_height: usize
}

impl Terminal {
    /// Creates a new Terminal from the provided filename
    pub fn from(filename: &str) -> io::Result<Terminal> {
        let file = OpenOptions::new().write(true).read(true).open(filename)?;
        let fd = file.as_raw_fd();

        let mut terminal = Terminal {
            file: file,
            prev_termios: None,
            max_width: 80,
            max_height: 25
        };

        unsafe {
            let mut ws: winsize = mem::zeroed();
            if ioctl(fd, TIOCGWINSZ, &mut ws) != -1 {
                terminal.max_width = ws.ws_col as usize;
                terminal.max_height = ws.ws_row as usize;
            }
        }

        Ok(terminal)
    }

    pub fn set_raw_mode(&mut self) -> Result<(), Error> {
        let fd = self.file.as_raw_fd();

        unsafe {
            let mut ios: termios = mem::zeroed();

            // get the existing termios
            if tcgetattr(fd, &mut ios) != 0 {
                return Err(Error::TcGetAttr);
            }

            self.prev_termios = Some(ios);

            // enable raw mode
            cfmakeraw(&mut ios);

            // apply the raw mode termios
            if tcsetattr(fd, TCSANOW, &ios) != 0 {
                return Err(Error::TcSetAttr);
            }
        }

        Ok(())
    }

    pub fn reset(&mut self) -> Result<(), Error> {
        if let Some(opts) = self.prev_termios {
            let fd = self.file.as_raw_fd();

            // disable raw mode, by setting the original termios
            unsafe {
                if tcsetattr(fd, TCSANOW, &opts) != 0 {
                    return Err(Error::TcSetAttr);
                }
            }
        }

        Ok(())
    }

    pub fn events(&self) -> io::Result<Events<File>> {
        self.file.try_clone().map(Events::new)
    }
}

impl Read for Terminal {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file.read(buf)
    }
}

impl Write for Terminal {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file.flush()
    }
}

impl Drop for Terminal {
    fn drop(&mut self) {
        self.reset().unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    #[test]
    fn terminal() {
        let mut term = Terminal::from("/dev/tty").expect("Unable to open /dev/tty");

        // if all is good, this should _not_ break the terminal, because the
        // Drop trait impl will clean up
        term.set_raw_mode().expect("Unable to enable raw mode");

        write!(term, "").unwrap();
        term.flush().unwrap();
    }
}
<|endoftext|>


<commit_before>

use types::cursor::XCursorImage;

use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wlroots_sys::{wl_list, wlr_output, wlr_output_events, wlr_output_layout,
                  wlr_output_layout_add_auto, wlr_output_layout_create, wlr_output_layout_destroy,
                  wlr_output_layout_remove, wlr_output_make_current, wlr_output_mode,
                  wlr_output_set_cursor, wlr_output_set_mode, wlr_output_swap_buffers};

use std::cell::RefCell;
use std::ffi::CStr;
use std::rc::Rc;

pub struct OutputState {
    pub layout: Option<Rc<RefCell<OutputLayout>>>
}

/// A wrapper around a wlr_output.
#[derive(Debug)]
pub struct Output {
    output: *mut wlr_output
}

#[derive(Debug)]
pub struct OutputLayout {
    layout: *mut wlr_output_layout
}

impl Output {
    pub unsafe fn set_user_data(&mut self, data: Rc<OutputState>) {
        (*self.output).data = Rc::into_raw(data) as *mut _
    }

    pub unsafe fn user_data(&mut self) -> *mut OutputState {
        (*self.output).data as *mut _
    }

    // TODO Prove all of this is sound, including user_data
    pub unsafe fn layout(&mut self) -> Option<Rc<RefCell<OutputLayout>>> {
        let data = self.user_data();
        if data.is_null() {
            None
        } else {
            (*data).layout.clone()
        }
    }

    pub fn add_layout_auto(&mut self, layout: Rc<RefCell<OutputLayout>>) {
        unsafe {
            wlr_output_layout_add_auto(layout.borrow_mut().to_ptr(), self.output);
            let user_data = self.user_data();
            if user_data.is_null() {
                self.set_user_data(Rc::new(OutputState {
                    layout: Some(layout)
                }));
            } else {
                (*user_data).layout = Some(layout);
            }
        }
    }

    pub fn set_cursor<'cursor>(&mut self, image: &'cursor XCursorImage<'cursor>) -> Result<(), ()> {
        unsafe {
            match wlr_output_set_cursor(self.output,
                                        image.buffer.as_ptr(),
                                        image.width as i32,
                                        image.width,
                                        image.height,
                                        image.hotspot_x as i32,
                                        image.hotspot_y as i32)
            {
                true => Ok(()),
                false => Err(())
            }
        }
    }

    /// Sets the best modesetting for an output.
    pub fn choose_best_mode(&mut self) {
        unsafe {
            let length = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_list_length, self.modes() as _);
            if length > 0 {
                // TODO Better logging
                wlr_log!(L_DEBUG, "output added {:?}", self);
                let first_mode_ptr: *mut wlr_output_mode;
                first_mode_ptr = container_of!(&mut (*(*self.modes()).prev) as *mut _,
                                               wlr_output_mode,
                                               link);
                wlr_output_set_mode(self.to_ptr(), first_mode_ptr);
            }
        }
    }

    /// Gets the name of the output in UTF-8.
    pub fn name(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).name.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Gets the make of the output in UTF-8.
    pub fn make(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).make.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Gets the model of the output in UTF-8.
    pub fn model(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).model.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    pub fn make_current(&mut self) {
        unsafe { wlr_output_make_current(self.output) }
    }

    pub fn swap_buffers(&mut self) {
        unsafe { wlr_output_swap_buffers(self.output) }
    }

    /// Get the dimensions of the output as (width, height).
    pub fn dimensions(&self) -> (i32, i32) {
        unsafe { ((*self.output).width, (*self.output).height) }
    }

    /// Get the physical dimensions of the output as (width, height).
    pub fn physical_dimensions(&self) -> (i32, i32) {
        unsafe { ((*self.output).phys_width, (*self.output).phys_height) }
    }

    // TODO Wrap this somehow? Hmm
    pub unsafe fn modes(&self) -> *mut wl_list {
        &mut (*self.output).modes
    }

    pub unsafe fn events(&self) -> wlr_output_events {
        (*self.output).events
    }

    pub unsafe fn from_ptr(output: *mut wlr_output) -> Self {
        Output { output }
    }

    pub unsafe fn to_ptr(&self) -> *mut wlr_output {
        self.output
    }
}

impl Drop for Output {
    fn drop(&mut self) {
        // TODO Implement
        // Also need to make sure it's not dropped except in the remove callback,
        // since right now there's actually a lot of Output destruction which we don't want
        // e.g need to make the from_ptr return ManuallyDrop<Output> now
    }
}

impl OutputLayout {
    pub fn new() -> Self {
        unsafe {
            OutputLayout {
                layout: wlr_output_layout_create()
            }
        }
    }

    pub unsafe fn to_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }

    pub unsafe fn from_ptr(layout: *mut wlr_output_layout) -> Self {
        OutputLayout { layout }
    }

    pub fn remove(&mut self, output: &mut Output) {
        // TODO Double check we can pass any output in here and not cause unsafety
        unsafe { wlr_output_layout_remove(self.layout, output.to_ptr()) }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        unsafe { wlr_output_layout_destroy(self.layout) }
    }
}
<commit_msg>Made OutputLayout.remove unsafe<commit_after>

use types::cursor::XCursorImage;

use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wlroots_sys::{wl_list, wlr_output, wlr_output_events, wlr_output_layout,
                  wlr_output_layout_add_auto, wlr_output_layout_create, wlr_output_layout_destroy,
                  wlr_output_layout_remove, wlr_output_make_current, wlr_output_mode,
                  wlr_output_set_cursor, wlr_output_set_mode, wlr_output_swap_buffers};

use std::cell::RefCell;
use std::ffi::CStr;
use std::rc::Rc;

pub struct OutputState {
    pub layout: Option<Rc<RefCell<OutputLayout>>>
}

/// A wrapper around a wlr_output.
#[derive(Debug)]
pub struct Output {
    output: *mut wlr_output
}

#[derive(Debug)]
pub struct OutputLayout {
    layout: *mut wlr_output_layout
}

impl Output {
    pub unsafe fn set_user_data(&mut self, data: Rc<OutputState>) {
        (*self.output).data = Rc::into_raw(data) as *mut _
    }

    pub unsafe fn user_data(&mut self) -> *mut OutputState {
        (*self.output).data as *mut _
    }

    // TODO Prove all of this is sound, including user_data
    pub unsafe fn layout(&mut self) -> Option<Rc<RefCell<OutputLayout>>> {
        let data = self.user_data();
        if data.is_null() {
            None
        } else {
            (*data).layout.clone()
        }
    }

    pub fn add_layout_auto(&mut self, layout: Rc<RefCell<OutputLayout>>) {
        unsafe {
            wlr_output_layout_add_auto(layout.borrow_mut().to_ptr(), self.output);
            let user_data = self.user_data();
            if user_data.is_null() {
                self.set_user_data(Rc::new(OutputState {
                    layout: Some(layout)
                }));
            } else {
                (*user_data).layout = Some(layout);
            }
        }
    }

    pub fn set_cursor<'cursor>(&mut self, image: &'cursor XCursorImage<'cursor>) -> Result<(), ()> {
        unsafe {
            match wlr_output_set_cursor(self.output,
                                        image.buffer.as_ptr(),
                                        image.width as i32,
                                        image.width,
                                        image.height,
                                        image.hotspot_x as i32,
                                        image.hotspot_y as i32)
            {
                true => Ok(()),
                false => Err(())
            }
        }
    }

    /// Sets the best modesetting for an output.
    pub fn choose_best_mode(&mut self) {
        unsafe {
            let length = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_list_length, self.modes() as _);
            if length > 0 {
                // TODO Better logging
                wlr_log!(L_DEBUG, "output added {:?}", self);
                let first_mode_ptr: *mut wlr_output_mode;
                first_mode_ptr = container_of!(&mut (*(*self.modes()).prev) as *mut _,
                                               wlr_output_mode,
                                               link);
                wlr_output_set_mode(self.to_ptr(), first_mode_ptr);
            }
        }
    }

    /// Gets the name of the output in UTF-8.
    pub fn name(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).name.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Gets the make of the output in UTF-8.
    pub fn make(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).make.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Gets the model of the output in UTF-8.
    pub fn model(&self) -> String {
        unsafe {
            CStr::from_ptr((*self.output).model.as_ptr())
                .to_string_lossy()
                .into_owned()
        }
    }

    pub fn make_current(&mut self) {
        unsafe { wlr_output_make_current(self.output) }
    }

    pub fn swap_buffers(&mut self) {
        unsafe { wlr_output_swap_buffers(self.output) }
    }

    /// Get the dimensions of the output as (width, height).
    pub fn dimensions(&self) -> (i32, i32) {
        unsafe { ((*self.output).width, (*self.output).height) }
    }

    /// Get the physical dimensions of the output as (width, height).
    pub fn physical_dimensions(&self) -> (i32, i32) {
        unsafe { ((*self.output).phys_width, (*self.output).phys_height) }
    }

    // TODO Wrap this somehow? Hmm
    pub unsafe fn modes(&self) -> *mut wl_list {
        &mut (*self.output).modes
    }

    pub unsafe fn events(&self) -> wlr_output_events {
        (*self.output).events
    }

    pub unsafe fn from_ptr(output: *mut wlr_output) -> Self {
        Output { output }
    }

    pub unsafe fn to_ptr(&self) -> *mut wlr_output {
        self.output
    }
}

impl Drop for Output {
    fn drop(&mut self) {
        // TODO Implement
        // Also need to make sure it's not dropped except in the remove callback,
        // since right now there's actually a lot of Output destruction which we don't want
        // e.g need to make the from_ptr return ManuallyDrop<Output> now
    }
}

impl OutputLayout {
    pub fn new() -> Self {
        unsafe {
            OutputLayout {
                layout: wlr_output_layout_create()
            }
        }
    }

    pub unsafe fn to_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }

    pub unsafe fn from_ptr(layout: *mut wlr_output_layout) -> Self {
        OutputLayout { layout }
    }

    /// # Unsafety
    /// The underlying function hasn't been proven to be stable if you
    /// pass it an invalid Output (e.g one that has already been freed).
    /// For now, this function is unsafe
    pub unsafe fn remove(&mut self, output: &mut Output) {
        // TODO Double check we can pass any output in here and not cause unsafety
        unsafe { wlr_output_layout_remove(self.layout, output.to_ptr()) }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        unsafe { wlr_output_layout_destroy(self.layout) }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    lsp_process_error::{LSPProcessError, LSPProcessResult},
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_executable_document_from_text,
};
use common::{PerfLogEvent, PerfLogger, Span};
use graphql_ir::Program;
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use lsp_types::{TextDocumentPositionParams, Url};
use relay_compiler::{
    compiler::Compiler, compiler_state::CompilerState, config::Config, FileCategorizer, FileSource,
    FileSourceSubscription,
};
use schema::Schema;
use std::{
    collections::{HashMap, HashSet},
    path::PathBuf,
    sync::{Arc, RwLock},
};

pub trait LSPExtraDataProvider {
    fn fetch_query_stats(&self, search_token: String) -> Vec<String>;
    fn resolve_field_definition(
        &self,
        project_name: String,
        root_dir: &PathBuf,
        parent_type: String,
        field_name: Option<String>,
    ) -> Option<Result<(String, u64), String>>;
}

pub(crate) struct LSPState<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    compiler: Option<Compiler<TPerfLogger>>,
    root_dir: PathBuf,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    file_categorizer: FileCategorizer,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
}

impl<TPerfLogger: PerfLogger + 'static> LSPState<TPerfLogger> {
    /// Private constructor
    fn new(config: Arc<Config>, extra_data_provider: Box<dyn LSPExtraDataProvider>) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        Self {
            config,
            compiler: None,
            extra_data_provider,
            file_categorizer,
            root_dir: root_dir.clone(),
            schemas: Default::default(),
            source_programs: Default::default(),
            synced_graphql_documents: Default::default(),
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) async fn create_state(
        config: Arc<Config>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        perf_logger: Arc<TPerfLogger>,
    ) -> LSPProcessResult<Self> {
        let mut lsp_state = Self::new(config, extra_data_provider);
        let setup_event = perf_logger.create_event("lsp_state_initialize_resources");
        let file_source = FileSource::connect(&lsp_state.config, &setup_event)
            .await
            .map_err(LSPProcessError::CompilerError)?;
        let (mut compiler_state, file_source_subscription) = file_source
            .subscribe(&setup_event, perf_logger.as_ref())
            .await
            .map_err(LSPProcessError::CompilerError)?;

        lsp_state.initial_build(&mut compiler_state, &setup_event, Arc::clone(&perf_logger))?;

        // Finally, start a dedicated watchman subscription, just for LSP to update schemas/programs in lsp_state
        lsp_state.watch_and_update_schemas(
            compiler_state,
            file_source_subscription,
            Arc::clone(&perf_logger),
        );

        // This is an instance of a regular Relay compiler that we will run in
        // watch mode in a separate future (it will report errors)

        perf_logger.complete_event(setup_event);
        perf_logger.flush();

        lsp_state.compiler = Some(Compiler::new(Arc::clone(&lsp_state.config), perf_logger));

        Ok(lsp_state)
    }

    fn watch_and_update_schemas(
        &mut self,
        mut compiler_state: CompilerState,
        mut subscription: FileSourceSubscription,
        perf_logger: Arc<TPerfLogger>,
    ) {
        let config = Arc::clone(&self.config);
        let compiler = Compiler::new(Arc::clone(&config), Arc::clone(&perf_logger));
        let source_programs = Arc::clone(&self.source_programs);
        let schemas = self.get_schemas();

        tokio::task::spawn(async move {
            loop {
                if let Some(file_source_changes) = subscription.next_change().await.unwrap() {
                    let log_event = perf_logger.create_event("lsp_state_watchman_event");
                    let log_time = log_event.start("lsp_state_watchman_event_time");

                    compiler_state
                        .pending_file_source_changes
                        .write()
                        .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                        .push(file_source_changes);

                    let has_new_changes = compiler_state
                        .merge_file_source_changes(&config, &log_event, perf_logger.as_ref(), false)
                        .unwrap_or_else(|err| {
                            Self::log_error(
                                &perf_logger,
                                format!("Unable to merge_file_source_changes: {:?}", err),
                            );
                            false
                        });

                    // If changes contains schema files we need to rebuild schemas
                    if has_new_changes && compiler_state.has_schema_changes() {
                        log_event.number("has_schema_change", 1);
                        let (next_schemas, build_schema_errors) =
                            compiler.build_schemas(&compiler_state, &log_event);
                        if !build_schema_errors.is_empty() {
                            Self::log_errors(&perf_logger, build_schema_errors);
                        }

                        schemas.write().expect("LSPState::watch_and_update_schemas: expect to acquire write lock on schemas").clone_from(&next_schemas);
                    } else {
                        log_event.number("has_schema_change", 0);
                    }

                    // Rebuilding programs
                    if has_new_changes {
                        Self::build_in_watch_mode(
                            &config,
                            &compiler,
                            &mut compiler_state,
                            &schemas,
                            &source_programs,
                            Arc::clone(&perf_logger),
                            &log_event,
                        )
                        .unwrap_or_else(|err| {
                            Self::log_error(
                                &perf_logger,
                                format!("Error in build_in_watch_mode: {:?}", err),
                            );
                        });
                    }

                    log_event.stop(log_time);
                    perf_logger.complete_event(log_event);
                    perf_logger.flush();
                }
            }
        });
    }

    pub(crate) fn get_schemas(&self) -> Arc<RwLock<HashMap<StringKey, Arc<Schema>>>> {
        self.schemas.clone()
    }

    pub(crate) fn get_source_programs_ref(&self) -> &Arc<RwLock<HashMap<StringKey, Program>>> {
        &self.source_programs
    }

    pub(crate) fn resolve_node(
        &self,
        params: TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            params,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    pub(crate) fn root_dir(&self) -> &PathBuf {
        &self.root_dir
    }

    pub(crate) fn insert_synced_sources(&mut self, url: Url, sources: Vec<GraphQLSource>) {
        self.start_compiler_once();
        self.synced_graphql_documents.insert(url, sources);
    }

    pub(crate) fn remove_synced_sources(&mut self, url: &Url) {
        self.synced_graphql_documents.remove(url);
    }

    pub(crate) fn extract_executable_document_from_text(
        &mut self,
        position: TextDocumentPositionParams,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span, StringKey)> {
        extract_executable_document_from_text(
            position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
            0,
        )
    }

    fn start_compiler_once(&mut self) {
        if let Some(compiler) = self.compiler.take() {
            tokio::spawn(async move { compiler.watch().await });
        }
    }

    fn initial_build(
        &mut self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
        perf_logger: Arc<TPerfLogger>,
    ) -> LSPProcessResult<()> {
        let compiler = Compiler::new(Arc::clone(&self.config), Arc::clone(&perf_logger));

        let (schemas, build_schema_errors) = compiler.build_schemas(&compiler_state, setup_event);

        if !build_schema_errors.is_empty() {
            Self::log_errors(&perf_logger, build_schema_errors);
        }

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (source_programs, build_raw_program_errors) =
            compiler.build_raw_programs(&compiler_state, &schemas, None, setup_event)?;

        if !build_raw_program_errors.is_empty() {
            Self::log_errors(&perf_logger, build_raw_program_errors);
        }

        compiler_state.complete_compilation();

        self.schemas = Arc::new(RwLock::new(schemas));
        self.source_programs = Arc::new(RwLock::new(source_programs));

        Ok(())
    }

    /// This function should handle internal watchman builds that will
    /// update schemas/source_programs every time those are created/updated
    fn build_in_watch_mode(
        config: &Arc<Config>,
        compiler: &Compiler<TPerfLogger>,
        compiler_state: &mut CompilerState,
        schemas: &Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
        source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
        perf_logger: Arc<TPerfLogger>,
        log_event: &impl PerfLogEvent,
    ) -> LSPProcessResult<()> {
        let timer = log_event.start("lsp_build_in_watch_mode");
        // we should trigger build only for changed projects
        // this set will be used in the `build_raw_programs` to ignore unchanged projects
        let affected_projects = config
            .projects
            .keys()
            .filter(|project_name| compiler_state.project_has_pending_changes(**project_name))
            .collect::<HashSet<_>>();

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (programs, build_raw_programs_errors) = compiler.build_raw_programs(
            &compiler_state,
            &schemas
                .read()
                .expect("LSPState::build_in_watch_mode: expect to acquire read lock on schemas"),
            Some(affected_projects),
            log_event,
        )?;

        if !build_raw_programs_errors.is_empty() {
            Self::log_errors(&perf_logger, build_raw_programs_errors)
        }

        let mut source_programs_write_lock = source_programs.write().expect(
            "LSPState::build_in_watch_mode: expect to acquire write lock on source_programs",
        );
        for (program_name, program) in programs {
            // NOTE: Currently, we rely on the fact that `build_raw_programs`
            // will always return a full program, so we can safely replace it in the current
            // list of source_programs
            source_programs_write_lock.insert(program_name, program);
        }

        compiler_state.complete_compilation();

        log_event.stop(timer);
        Ok(())
    }

    fn log_errors<T: core::fmt::Debug>(logger: &Arc<TPerfLogger>, build_errors: Vec<T>) {
        Self::log_error(
            logger,
            build_errors
                .iter()
                .map(|err| format!("{:?}", err))
                .collect::<Vec<String>>()
                .join("\n"),
        );
    }

    /// A quick helper so we can unwrap things and log, if somethings isn't right
    fn log_error(logger: &Arc<TPerfLogger>, error_message: String) {
        let error_event = logger.create_event("lsp_state_error");
        error_event.string("message", error_message);
        logger.complete_event(error_event);
        logger.flush();
    }
}
<commit_msg>Log time for LSP state initialization, and time for updating programs cache<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    lsp_process_error::{LSPProcessError, LSPProcessResult},
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_executable_document_from_text,
};
use common::{PerfLogEvent, PerfLogger, Span};
use graphql_ir::Program;
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use lsp_types::{TextDocumentPositionParams, Url};
use relay_compiler::{
    compiler::Compiler, compiler_state::CompilerState, config::Config, FileCategorizer, FileSource,
    FileSourceSubscription,
};
use schema::Schema;
use std::{
    collections::{HashMap, HashSet},
    path::PathBuf,
    sync::{Arc, RwLock},
};

pub trait LSPExtraDataProvider {
    fn fetch_query_stats(&self, search_token: String) -> Vec<String>;
    fn resolve_field_definition(
        &self,
        project_name: String,
        root_dir: &PathBuf,
        parent_type: String,
        field_name: Option<String>,
    ) -> Option<Result<(String, u64), String>>;
}

pub(crate) struct LSPState<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    compiler: Option<Compiler<TPerfLogger>>,
    root_dir: PathBuf,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    file_categorizer: FileCategorizer,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
    synced_graphql_documents: HashMap<Url, Vec<GraphQLSource>>,
}

impl<TPerfLogger: PerfLogger + 'static> LSPState<TPerfLogger> {
    /// Private constructor
    fn new(config: Arc<Config>, extra_data_provider: Box<dyn LSPExtraDataProvider>) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        Self {
            config,
            compiler: None,
            extra_data_provider,
            file_categorizer,
            root_dir: root_dir.clone(),
            schemas: Default::default(),
            source_programs: Default::default(),
            synced_graphql_documents: Default::default(),
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) async fn create_state(
        config: Arc<Config>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        perf_logger: Arc<TPerfLogger>,
    ) -> LSPProcessResult<Self> {
        let mut lsp_state = Self::new(config, extra_data_provider);
        let setup_event = perf_logger.create_event("lsp_state_initialize_resources");
        let log_time = setup_event.start("lsp_state_initialize_resources_time");

        let file_source = FileSource::connect(&lsp_state.config, &setup_event)
            .await
            .map_err(LSPProcessError::CompilerError)?;
        let (mut compiler_state, file_source_subscription) = file_source
            .subscribe(&setup_event, perf_logger.as_ref())
            .await
            .map_err(LSPProcessError::CompilerError)?;

        lsp_state.initial_build(&mut compiler_state, &setup_event, Arc::clone(&perf_logger))?;

        // Finally, start a dedicated watchman subscription, just for LSP to update schemas/programs in lsp_state
        lsp_state.watch_and_update_schemas(
            compiler_state,
            file_source_subscription,
            Arc::clone(&perf_logger),
        );

        // This is an instance of a regular Relay compiler that we will run in
        // watch mode in a separate future (it will report errors)
        setup_event.stop(log_time);
        perf_logger.complete_event(setup_event);
        perf_logger.flush();

        lsp_state.compiler = Some(Compiler::new(Arc::clone(&lsp_state.config), perf_logger));

        Ok(lsp_state)
    }

    fn watch_and_update_schemas(
        &mut self,
        mut compiler_state: CompilerState,
        mut subscription: FileSourceSubscription,
        perf_logger: Arc<TPerfLogger>,
    ) {
        let config = Arc::clone(&self.config);
        let compiler = Compiler::new(Arc::clone(&config), Arc::clone(&perf_logger));
        let source_programs = Arc::clone(&self.source_programs);
        let schemas = self.get_schemas();

        tokio::task::spawn(async move {
            loop {
                if let Some(file_source_changes) = subscription.next_change().await.unwrap() {
                    let log_event = perf_logger.create_event("lsp_state_watchman_event");
                    let log_time = log_event.start("lsp_state_watchman_event_time");

                    compiler_state
                        .pending_file_source_changes
                        .write()
                        .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                        .push(file_source_changes);

                    let has_new_changes = compiler_state
                        .merge_file_source_changes(&config, &log_event, perf_logger.as_ref(), false)
                        .unwrap_or_else(|err| {
                            Self::log_error(
                                &perf_logger,
                                format!("Unable to merge_file_source_changes: {:?}", err),
                            );
                            false
                        });

                    // If changes contains schema files we need to rebuild schemas
                    if has_new_changes && compiler_state.has_schema_changes() {
                        log_event.number("has_schema_change", 1);
                        let (next_schemas, build_schema_errors) =
                            compiler.build_schemas(&compiler_state, &log_event);
                        if !build_schema_errors.is_empty() {
                            Self::log_errors(&perf_logger, build_schema_errors);
                        }

                        schemas.write().expect("LSPState::watch_and_update_schemas: expect to acquire write lock on schemas").clone_from(&next_schemas);
                    } else {
                        log_event.number("has_schema_change", 0);
                    }

                    // Rebuilding programs
                    if has_new_changes {
                        Self::build_in_watch_mode(
                            &config,
                            &compiler,
                            &mut compiler_state,
                            &schemas,
                            &source_programs,
                            Arc::clone(&perf_logger),
                            &log_event,
                        )
                        .unwrap_or_else(|err| {
                            Self::log_error(
                                &perf_logger,
                                format!("Error in build_in_watch_mode: {:?}", err),
                            );
                        });
                    }

                    log_event.stop(log_time);
                    perf_logger.complete_event(log_event);
                    perf_logger.flush();
                }
            }
        });
    }

    pub(crate) fn get_schemas(&self) -> Arc<RwLock<HashMap<StringKey, Arc<Schema>>>> {
        self.schemas.clone()
    }

    pub(crate) fn get_source_programs_ref(&self) -> &Arc<RwLock<HashMap<StringKey, Program>>> {
        &self.source_programs
    }

    pub(crate) fn resolve_node(
        &self,
        params: TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            params,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    pub(crate) fn root_dir(&self) -> &PathBuf {
        &self.root_dir
    }

    pub(crate) fn insert_synced_sources(&mut self, url: Url, sources: Vec<GraphQLSource>) {
        self.start_compiler_once();
        self.synced_graphql_documents.insert(url, sources);
    }

    pub(crate) fn remove_synced_sources(&mut self, url: &Url) {
        self.synced_graphql_documents.remove(url);
    }

    pub(crate) fn extract_executable_document_from_text(
        &mut self,
        position: TextDocumentPositionParams,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span, StringKey)> {
        extract_executable_document_from_text(
            position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
            0,
        )
    }

    fn start_compiler_once(&mut self) {
        if let Some(compiler) = self.compiler.take() {
            tokio::spawn(async move { compiler.watch().await });
        }
    }

    fn initial_build(
        &mut self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
        perf_logger: Arc<TPerfLogger>,
    ) -> LSPProcessResult<()> {
        let compiler = Compiler::new(Arc::clone(&self.config), Arc::clone(&perf_logger));

        let (schemas, build_schema_errors) = compiler.build_schemas(&compiler_state, setup_event);

        if !build_schema_errors.is_empty() {
            Self::log_errors(&perf_logger, build_schema_errors);
        }

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (source_programs, build_raw_program_errors) =
            compiler.build_raw_programs(&compiler_state, &schemas, None, setup_event)?;

        if !build_raw_program_errors.is_empty() {
            Self::log_errors(&perf_logger, build_raw_program_errors);
        }

        compiler_state.complete_compilation();

        self.schemas = Arc::new(RwLock::new(schemas));
        self.source_programs = Arc::new(RwLock::new(source_programs));

        Ok(())
    }

    /// This function should handle internal watchman builds that will
    /// update schemas/source_programs every time those are created/updated
    fn build_in_watch_mode(
        config: &Arc<Config>,
        compiler: &Compiler<TPerfLogger>,
        compiler_state: &mut CompilerState,
        schemas: &Arc<RwLock<HashMap<StringKey, Arc<Schema>>>>,
        source_programs: &Arc<RwLock<HashMap<StringKey, Program>>>,
        perf_logger: Arc<TPerfLogger>,
        log_event: &impl PerfLogEvent,
    ) -> LSPProcessResult<()> {
        // we should trigger build only for changed projects
        // this set will be used in the `build_raw_programs` to ignore unchanged projects
        let affected_projects = config
            .projects
            .keys()
            .filter(|project_name| compiler_state.project_has_pending_changes(**project_name))
            .collect::<HashSet<_>>();

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (programs, build_raw_programs_errors) = compiler.build_raw_programs(
            &compiler_state,
            &schemas
                .read()
                .expect("LSPState::build_in_watch_mode: expect to acquire read lock on schemas"),
            Some(affected_projects),
            log_event,
        )?;

        if !build_raw_programs_errors.is_empty() {
            Self::log_errors(&perf_logger, build_raw_programs_errors)
        }
        let timer = log_event.start("lsp_build_in_watch_writing_programs_time");

        let mut source_programs_write_lock = source_programs.write().expect(
            "LSPState::build_in_watch_mode: expect to acquire write lock on source_programs",
        );
        for (program_name, program) in programs {
            // NOTE: Currently, we rely on the fact that `build_raw_programs`
            // will always return a full program, so we can safely replace it in the current
            // list of source_programs
            source_programs_write_lock.insert(program_name, program);
        }

        compiler_state.complete_compilation();

        log_event.stop(timer);
        Ok(())
    }

    fn log_errors<T: core::fmt::Debug>(logger: &Arc<TPerfLogger>, build_errors: Vec<T>) {
        Self::log_error(
            logger,
            build_errors
                .iter()
                .map(|err| format!("{:?}", err))
                .collect::<Vec<String>>()
                .join("\n"),
        );
    }

    /// A quick helper so we can unwrap things and log, if somethings isn't right
    fn log_error(logger: &Arc<TPerfLogger>, error_message: String) {
        let error_event = logger.create_event("lsp_state_error");
        error_event.string("message", error_message);
        logger.complete_event(error_event);
        logger.flush();
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword(
    "dominant-baseline",
    """auto use-script no-change reset-size ideographic alphabetic hanging
    mathematical central middle text-after-edge text-before-edge""",
    products="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty",
)}

${helpers.single_keyword(
    "vector-effect",
    "none non-scaling-stroke",
    products="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
)}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type(
    "stop-opacity",
    "Opacity",
    "1.0",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity",
)}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type(
    "flood-opacity",
    "Opacity",
    "1.0",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty",
)}

${helpers.predefined_type(
    "lighting-color",
    "Color",
    "RGBA::new(255, 255, 255, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword(
    "mask-type",
    "luminance alpha",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
)}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    products="gecko",
    boxed=True,
    animation_value_type="basic_shape::ClippingShape",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword(
    "mask-mode",
    "match-source alpha luminance",
    vector=True,
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
)}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        "computed::LengthPercentage::zero()",
        products="gecko",
        extra_prefixes="webkit",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    products="gecko",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword(
    "mask-composite",
    "add subtract intersect exclude",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
)}

${helpers.predefined_type(
    "mask-image",
    "ImageLayer",
    "Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    parse_method="parse_with_cors_anonymous",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT",
)}
<commit_msg>style: Convert NS_STYLE_MASK_MODE_* to enum class.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword(
    "dominant-baseline",
    """auto use-script no-change reset-size ideographic alphabetic hanging
    mathematical central middle text-after-edge text-before-edge""",
    products="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty",
)}

${helpers.single_keyword(
    "vector-effect",
    "none non-scaling-stroke",
    products="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
)}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type(
    "stop-opacity",
    "Opacity",
    "1.0",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity",
)}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type(
    "flood-opacity",
    "Opacity",
    "1.0",
    products="gecko",
    animation_value_type="ComputedValue",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty",
)}

${helpers.predefined_type(
    "lighting-color",
    "Color",
    "RGBA::new(255, 255, 255, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword(
    "mask-type",
    "luminance alpha",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
)}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    products="gecko",
    boxed=True,
    animation_value_type="basic_shape::ClippingShape",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword(
    "mask-mode",
    "match-source alpha luminance",
    gecko_enum_prefix="StyleMaskMode",
    vector=True,
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
)}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        "computed::LengthPercentage::zero()",
        products="gecko",
        extra_prefixes="webkit",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    products="gecko",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword(
    "mask-composite",
    "add subtract intersect exclude",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
)}

${helpers.predefined_type(
    "mask-image",
    "ImageLayer",
    "Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    parse_method="parse_with_cors_anonymous",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT",
)}
<|endoftext|>


<commit_before>extern crate alfred;

use config::Config;
use issue::Issue;
use jira_api::SearchIssue;
use jira_client;

struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<String, String> {
        let jira_search = SearchIssue{
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let issues = jira_client::send::<SearchIssue, Issue>(jira_search).map_err(|e| e.to_string())?;
        Ok("".to_string())
    }
}<commit_msg>:art: Change return value to the type SearchResponse<commit_after>extern crate alfred;

use config::Config;
use jira_api::SearchIssue;
use jira_client;
use search_response::SearchResponse;

struct SearchCommand {
    pub config: Config,
}

impl SearchCommand {
    pub fn run(&self, keyword: &String) -> Result<String, String> {
        let jira_search = SearchIssue{
            config: self.config.clone(),
            keyword: keyword.to_string(),
        };
        let issues = jira_client::send::<SearchIssue, SearchResponse>(jira_search).map_err(|e| e.to_string())?;
        Ok("".to_string())
    }
}<|endoftext|>