<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use sys::MTLFeatureSet;
use {Buffer, CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, ResourceOptions, Size, Texture, TextureDescriptor};

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil { Ok(Device(device)) } else { Err(DeviceError::ConstructionFailed) }
    }

    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_feature_set(&self, feature_set: FeatureSet) -> bool {
        unsafe { self.0.supportsFeatureSet(feature_set.into()) == YES }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_library_with_file(&mut self, file_path: &AsRef<Path>)
                                 -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source(&mut self, source: &AsRef<str>, compile_options: &CompileOptions)
                                   -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_buffer_with_length_and_options(&mut self, length: usize, options: ResourceOptions)
                                              -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_buffer_with_bytes_options(&mut self, bytes: &[u8], options: ResourceOptions)
                                         -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_texture(&mut self, descriptor: &TextureDescriptor) -> Result<Texture, FromRawError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): Model types correctly
    pub fn new_sampler_state(&mut self, descriptor: ()) -> ! {
        unimplemented!();
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

#[cfg(target_os = "ios")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        iOSGpuFamily1_v1 => MTLFeatureSet_iOS_GPUFamily1_v1,
        iOSGpuFamily2_v1 => MTLFeatureSet_iOS_GPUFamily2_v1,
        iOSGpuFamily1_v2 => MTLFeatureSet_iOS_GPUFamily1_v2,
        iOSGpuFamily2_v2 => MTLFeatureSet_iOS_GPUFamily2_v2,
        iOSGpuFamily3_v1 => MTLFeatureSet_iOS_GPUFamily3_v1
    }
}

#[cfg(target_os = "macos")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        OsxGpuFamily1_v1 => MTLFeatureSet_OSX_GPUFamily1_v1,
        _non_unary_compile_dummy => _non_unary_compile_dummy
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    RawError(FromRawError)
}

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "DeviceError::ConstructionFailed",
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed => {
                "Could not create a default device. Please ensure that you are using at least OSX \
                 10.11 or iOS 8.0"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<commit_msg>More AsRef removal<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::From;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use sys::MTLFeatureSet;
use {Buffer, CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, ResourceOptions, Size, Texture, TextureDescriptor};

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil { Ok(Device(device)) } else { Err(DeviceError::ConstructionFailed) }
    }

    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_feature_set(&self, feature_set: FeatureSet) -> bool {
        unsafe { self.0.supportsFeatureSet(feature_set.into()) == YES }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_library_with_file(&mut self, file_path: &Path) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source(&mut self, source: &str, compile_options: &CompileOptions)
                                   -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_buffer_with_length_and_options(&mut self, length: usize, options: ResourceOptions)
                                              -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_buffer_with_bytes_options(&mut self, bytes: &[u8], options: ResourceOptions)
                                         -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_texture(&mut self, descriptor: &TextureDescriptor) -> Result<Texture, FromRawError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): Model types correctly
    pub fn new_sampler_state(&mut self, descriptor: ()) -> ! {
        unimplemented!();
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

#[cfg(target_os = "ios")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        iOSGpuFamily1_v1 => MTLFeatureSet_iOS_GPUFamily1_v1,
        iOSGpuFamily2_v1 => MTLFeatureSet_iOS_GPUFamily2_v1,
        iOSGpuFamily1_v2 => MTLFeatureSet_iOS_GPUFamily1_v2,
        iOSGpuFamily2_v2 => MTLFeatureSet_iOS_GPUFamily2_v2,
        iOSGpuFamily3_v1 => MTLFeatureSet_iOS_GPUFamily3_v1
    }
}

#[cfg(target_os = "macos")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        OsxGpuFamily1_v1 => MTLFeatureSet_OSX_GPUFamily1_v1,
        _non_unary_compile_dummy => _non_unary_compile_dummy
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    RawError(FromRawError)
}

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "DeviceError::ConstructionFailed",
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed => {
                "Could not create a default device. Please ensure that you are using at least OSX \
                 10.11 or iOS 8.0"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use sys::MTLFeatureSet;
use {CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, Size};

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil { Ok(Device(device)) } else { Err(DeviceError::ConstructionFailed) }
    }

    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_feature_set(&self, feature_set: FeatureSet) -> bool {
        unsafe { self.0.supportsFeatureSet(feature_set.into()) == YES }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_library_with_file(&mut self, file_path: &AsRef<Path>)
                                 -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source(&mut self, source: &AsRef<str>, compile_options: &CompileOptions)
                                   -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(George): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

#[cfg(target_os = "ios")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        iOSGpuFamily1_v1 => MTLFeatureSet_iOS_GPUFamily1_v1,
        iOSGpuFamily2_v1 => MTLFeatureSet_iOS_GPUFamily2_v1,
        iOSGpuFamily1_v2 => MTLFeatureSet_iOS_GPUFamily1_v2,
        iOSGpuFamily2_v2 => MTLFeatureSet_iOS_GPUFamily2_v2,
        iOSGpuFamily3_v1 => MTLFeatureSet_iOS_GPUFamily3_v1
    }
}

#[cfg(target_os = "macos")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        OsxGpuFamily1_v1 => MTLFeatureSet_OSX_GPUFamily1_v1,
        _non_unary_compile_dummy => _non_unary_compile_dummy
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    RawError(FromRawError)
}

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "DeviceError::ConstructionFailed",
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed => {
                "Could not create a default device. Please ensure that you are using at least OSX \
                 10.11 or iOS 8.0"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<commit_msg>Add more stub methods<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use sys::MTLFeatureSet;
use {Buffer, CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, ResourceOptions, Size, Texture, TextureDescriptor};

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil { Ok(Device(device)) } else { Err(DeviceError::ConstructionFailed) }
    }

    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    pub fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn supports_feature_set(&self, feature_set: FeatureSet) -> bool {
        unsafe { self.0.supportsFeatureSet(feature_set.into()) == YES }
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_library_with_file(&mut self, file_path: &AsRef<Path>)
                                 -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source(&mut self, source: &AsRef<str>, compile_options: &CompileOptions)
                                   -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_buffer_with_length_and_options(&mut self, length: usize, options: ResourceOptions)
                                              -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    #[cfg_attr(rustfmt, rustfmt_skip)]
    pub fn new_buffer_with_bytes_options(&mut self, bytes: &[u8], options: ResourceOptions)
                                         -> Buffer {
        unimplemented!();
    }

    #[allow(unused_variables)]
    pub fn new_texture(&mut self, descriptor: &TextureDescriptor) -> Result<Texture, FromRawError> {
        unimplemented!();
    }

    #[allow(unused_variables)]
    // TODO(burtonageo): Model types correctly
    pub fn new_sampler_state(&mut self, descriptor: ()) -> ! {
        unimplemented!();
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

#[cfg(target_os = "ios")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        iOSGpuFamily1_v1 => MTLFeatureSet_iOS_GPUFamily1_v1,
        iOSGpuFamily2_v1 => MTLFeatureSet_iOS_GPUFamily2_v1,
        iOSGpuFamily1_v2 => MTLFeatureSet_iOS_GPUFamily1_v2,
        iOSGpuFamily2_v2 => MTLFeatureSet_iOS_GPUFamily2_v2,
        iOSGpuFamily3_v1 => MTLFeatureSet_iOS_GPUFamily3_v1
    }
}

#[cfg(target_os = "macos")]
convertible_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Eq, Hash, PartialEq)]
    pub enum FeatureSet: MTLFeatureSet {
        OsxGpuFamily1_v1 => MTLFeatureSet_OSX_GPUFamily1_v1,
        _non_unary_compile_dummy => _non_unary_compile_dummy
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    RawError(FromRawError)
}

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "DeviceError::ConstructionFailed",
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed => {
                "Could not create a default device. Please ensure that you are using at least OSX \
                 10.11 or iOS 8.0"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate serde_json;
extern crate varlink;
extern crate varlink_parser;

#[macro_use]
extern crate error_chain;

use clap::{App, Arg, SubCommand};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::str;
use varlink::{Connection, GetInterfaceDescriptionReply, MethodCall, OrgVarlinkServiceClient,
              OrgVarlinkServiceInterface};
use varlink_parser::Varlink;

mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
            Fmt(::std::fmt::Error);
            SerdeJson(::serde_json::Error);
            Clap(::clap::Error);
        }
        links {
            Varlink(::varlink::Error, ::varlink::ErrorKind);
        }
        errors {
            NotImplemented(t: String) {
                display("Not yet implemented: '{}'", t)
            }
        }
    }
}

use errors::*;

fn varlink_format(filename: &str) -> Result<()> {
    let mut buffer = String::new();
    File::open(Path::new(filename))?.read_to_string(&mut buffer)?;

    let v = Varlink::from_string(&buffer)?;
    println!("{}", v.interface);
    Ok(())
}

fn varlink_info(address: &str) -> Result<()> {
    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    let info = call.get_info()?;
    println!("Vendor: {}", info.vendor);
    println!("Product: {}", info.product);
    println!("Version: {}", info.version);
    println!("URL: {}", info.url);
    println!("Interfaces:");
    for i in info.interfaces {
        println!("  {}", i)
    }

    Ok(())
}

fn varlink_help(url: &str) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let interface: &str;

    match del.len() {
        2 => {
            address = del[0];
            interface = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    match call.get_interface_description(interface.into())? {
        GetInterfaceDescriptionReply {
            description: Some(desc),
        } => println!("{}", desc),
        _ => {
            return Err(ErrorKind::NotImplemented(format!("No description for {}", url)).into());
        }
    };

    Ok(())
}

fn varlink_call(url: &str, args: Option<&str>, more: bool) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let method: &str;

    match del.len() {
        2 => {
            address = del[0];
            method = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let args = match args {
        Some(args) => serde_json::from_str(args)?,
        None => serde_json::Value::Null,
    };

    let mut call = MethodCall::<serde_json::Value, serde_json::Value, varlink::Error>::new(
        conn.clone(),
        String::from(method).into(),
        args,
    );

    if !more {
        let reply = call.call()?;
        println!("{}", serde_json::to_string_pretty(&reply)?);
    } else {
        for reply in call.more()? {
            println!("{}", serde_json::to_string_pretty(&reply?)?);
        }
    }

    Ok(())
}

fn main() -> Result<()> {
    let mut app = App::new("varlink")
        .version("0.1")
        .arg(
            Arg::with_name("timeout")
                .short("t")
                .long("timeout")
                .value_name("SECONDS")
                .help("time in seconds to wait for a reply")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("resolver")
                .short("R")
                .long("resolver")
                .value_name("ADDRESS")
                .help("address of the resolver")
                .takes_value(true),
        )
        .subcommand(
            SubCommand::with_name("bridge")
                .about("Bridge varlink messages to services on this machine")
                .long_about(
                    "Bridge varlink messages on standard in and out to varlink services on this \
                     machine.",
                ),
        )
        .subcommand(
            SubCommand::with_name("call")
                .about("Call a method")
                .long_about("Call METHOD on INTERFACE at ADDRESS. ARGUMENTS must be valid JSON.")
                .arg(
                    Arg::with_name("more")
                        .short("m")
                        .long("more")
                        .help("wait for multiple method returns if supported"),
                )
                .arg(
                    Arg::with_name("METHOD")
                        .value_name("[ADDRESS/]INTERFACE.METHOD")
                        .required(true),
                )
                .arg(Arg::with_name("ARGUMENTS").required(false)),
        )
        .subcommand(
            SubCommand::with_name("format")
                .about("Format a varlink service file")
                .arg(
                    Arg::with_name("FILE")
                        .required(true)
                        .help("The varlink interface definition file to format"),
                ),
        )
        .subcommand(
            SubCommand::with_name("info")
                .about("Print information about a service")
                .long_about("Prints information about the service running at ADDRESS.")
                .arg(Arg::with_name("ADDRESS").required(true)),
        )
        .subcommand(
            SubCommand::with_name("help")
                .about("Print interface description or service information")
                .long_about("Prints information about INTERFACE.")
                .arg(
                    Arg::with_name("INTERFACE")
                        .value_name("[ADDRESS/]INTERFACE")
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("resolve")
                .about("Resolve an interface name to a varlink address")
                .long_about("Resolve INTERFACE to the varlink address that implements it.")
                .arg(Arg::with_name("INTERFACE").required(true)),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Generates completion scripts for your shell")
                .arg(
                    Arg::with_name("SHELL")
                        .required(true)
                        .possible_values(&["bash", "fish", "zsh"])
                        .help("The shell to generate the script for"),
                ),
        );
    let matches = app.clone().get_matches();

    match matches.subcommand() {
        ("completions", Some(sub_matches)) => {
            let shell = sub_matches.value_of("SHELL").unwrap();
            app.gen_completions_to("varlink", shell.parse().unwrap(), &mut io::stdout());
        }
        ("format", Some(sub_matches)) => {
            let filename = sub_matches.value_of("FILE").unwrap();
            varlink_format(filename)?
        }
        ("info", Some(sub_matches)) => {
            let address = sub_matches.value_of("ADDRESS").unwrap();
            varlink_info(address)?
        }
        ("help", Some(sub_matches)) => {
            let interface = sub_matches.value_of("INTERFACE").unwrap();
            varlink_help(interface)?
        }
        ("call", Some(sub_matches)) => {
            let method = sub_matches.value_of("METHOD").unwrap();
            let args = sub_matches.value_of("ARGUMENTS");
            let more = sub_matches.is_present("more");
            varlink_call(method, args, more)?
        }
        (_, _) => app.print_help()?,
    }
    Ok(())
}
<commit_msg>use error_chain<commit_after>extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate serde_json;
extern crate varlink;
extern crate varlink_parser;

use clap::{App, Arg, SubCommand};
use errors::*;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::str;
use varlink::{Connection, GetInterfaceDescriptionReply, MethodCall, OrgVarlinkServiceClient,
              OrgVarlinkServiceInterface};
use varlink_parser::Varlink;

mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
            Fmt(::std::fmt::Error);
            SerdeJson(::serde_json::Error);
            Clap(::clap::Error);
        }
        links {
            Varlink(::varlink::Error, ::varlink::ErrorKind);
            VarlinkParse(::varlink_parser::Error, ::varlink_parser::ErrorKind);
        }
        errors {
            NotImplemented(t: String) {
                display("Not yet implemented: '{}'", t)
            }
        }
    }
}

fn varlink_format(filename: &str) -> Result<()> {
    let mut buffer = String::new();
    File::open(Path::new(filename))?.read_to_string(&mut buffer)?;

    let v = Varlink::from_string(&buffer)?;
    println!("{}", v.interface);
    Ok(())
}

fn varlink_info(address: &str) -> Result<()> {
    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    let info = call.get_info()?;
    println!("Vendor: {}", info.vendor);
    println!("Product: {}", info.product);
    println!("Version: {}", info.version);
    println!("URL: {}", info.url);
    println!("Interfaces:");
    for i in info.interfaces {
        println!("  {}", i)
    }

    Ok(())
}

fn varlink_help(url: &str) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let interface: &str;

    match del.len() {
        2 => {
            address = del[0];
            interface = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    match call.get_interface_description(interface.into())? {
        GetInterfaceDescriptionReply {
            description: Some(desc),
        } => println!("{}", desc),
        _ => {
            return Err(ErrorKind::NotImplemented(format!("No description for {}", url)).into());
        }
    };

    Ok(())
}

fn varlink_call(url: &str, args: Option<&str>, more: bool) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let method: &str;

    match del.len() {
        2 => {
            address = del[0];
            method = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let args = match args {
        Some(args) => serde_json::from_str(args)?,
        None => serde_json::Value::Null,
    };

    let mut call = MethodCall::<serde_json::Value, serde_json::Value, varlink::Error>::new(
        conn.clone(),
        String::from(method).into(),
        args,
    );

    if !more {
        let reply = call.call()?;
        println!("{}", serde_json::to_string_pretty(&reply)?);
    } else {
        for reply in call.more()? {
            println!("{}", serde_json::to_string_pretty(&reply?)?);
        }
    }

    Ok(())
}

fn main() -> Result<()> {
    let mut app = App::new("varlink")
        .version("0.1")
        .arg(
            Arg::with_name("timeout")
                .short("t")
                .long("timeout")
                .value_name("SECONDS")
                .help("time in seconds to wait for a reply")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("resolver")
                .short("R")
                .long("resolver")
                .value_name("ADDRESS")
                .help("address of the resolver")
                .takes_value(true),
        )
        .subcommand(
            SubCommand::with_name("bridge")
                .about("Bridge varlink messages to services on this machine")
                .long_about(
                    "Bridge varlink messages on standard in and out to varlink services on this \
                     machine.",
                ),
        )
        .subcommand(
            SubCommand::with_name("call")
                .about("Call a method")
                .long_about("Call METHOD on INTERFACE at ADDRESS. ARGUMENTS must be valid JSON.")
                .arg(
                    Arg::with_name("more")
                        .short("m")
                        .long("more")
                        .help("wait for multiple method returns if supported"),
                )
                .arg(
                    Arg::with_name("METHOD")
                        .value_name("[ADDRESS/]INTERFACE.METHOD")
                        .required(true),
                )
                .arg(Arg::with_name("ARGUMENTS").required(false)),
        )
        .subcommand(
            SubCommand::with_name("format")
                .about("Format a varlink service file")
                .arg(
                    Arg::with_name("FILE")
                        .required(true)
                        .help("The varlink interface definition file to format"),
                ),
        )
        .subcommand(
            SubCommand::with_name("info")
                .about("Print information about a service")
                .long_about("Prints information about the service running at ADDRESS.")
                .arg(Arg::with_name("ADDRESS").required(true)),
        )
        .subcommand(
            SubCommand::with_name("help")
                .about("Print interface description or service information")
                .long_about("Prints information about INTERFACE.")
                .arg(
                    Arg::with_name("INTERFACE")
                        .value_name("[ADDRESS/]INTERFACE")
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("resolve")
                .about("Resolve an interface name to a varlink address")
                .long_about("Resolve INTERFACE to the varlink address that implements it.")
                .arg(Arg::with_name("INTERFACE").required(true)),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Generates completion scripts for your shell")
                .arg(
                    Arg::with_name("SHELL")
                        .required(true)
                        .possible_values(&["bash", "fish", "zsh"])
                        .help("The shell to generate the script for"),
                ),
        );
    let matches = app.clone().get_matches();

    match matches.subcommand() {
        ("completions", Some(sub_matches)) => {
            let shell = sub_matches.value_of("SHELL").unwrap();
            app.gen_completions_to("varlink", shell.parse().unwrap(), &mut io::stdout());
        }
        ("format", Some(sub_matches)) => {
            let filename = sub_matches.value_of("FILE").unwrap();
            varlink_format(filename)?
        }
        ("info", Some(sub_matches)) => {
            let address = sub_matches.value_of("ADDRESS").unwrap();
            varlink_info(address)?
        }
        ("help", Some(sub_matches)) => {
            let interface = sub_matches.value_of("INTERFACE").unwrap();
            varlink_help(interface)?
        }
        ("call", Some(sub_matches)) => {
            let method = sub_matches.value_of("METHOD").unwrap();
            let args = sub_matches.value_of("ARGUMENTS");
            let more = sub_matches.is_present("more");
            varlink_call(method, args, more)?
        }
        (_, _) => app.print_help()?,
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> {
    let package = ws.current()?;

    let spec = match options.compile_opts.spec {
        ops::Packages::Packages(packages) => packages,
        _ => {
            // This should not happen, because the `doc` binary is hard-coded to pass
            // the `Packages::Packages` variant.
            bail!("`cargo doc` does not support the `--all` flag")
        },
    };

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    ops::compile(ws, &options.compile_opts)?;

    if options.open_result {
        let name = if spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if spec.len() == 1 {
            PackageIdSpec::parse(&spec[0])?
                .name()
                .replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(()),
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let mut target_dir = ws.target_dir();
        if let Some(triple) = options.compile_opts.target {
            target_dir.push(Path::new(triple).file_stem().unwrap());
        }
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => shell.status("Launching", m)?,
                Err(e) => {
                    shell.warn(
                            "warning: could not determine a browser to open docs with, tried:")?;
                    for method in e {
                        shell.warn(format!("\t{}", method))?;
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    if let Ok(name) = env::var("BROWSER") {
        match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER"),
        }
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m),
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => return Ok("cmd /C"),
        Err(_) => return Err(vec!["cmd /C"]),
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"]),
    };
}
<commit_msg>cargo doc: show where are docs<commit_after>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{PackageIdSpec, Workspace};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> {
    let package = ws.current()?;

    let spec = match options.compile_opts.spec {
        ops::Packages::Packages(packages) => packages,
        _ => {
            // This should not happen, because the `doc` binary is hard-coded to pass
            // the `Packages::Packages` variant.
            bail!("`cargo doc` does not support the `--all` flag")
        },
    };

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    ops::compile(ws, &options.compile_opts)?;

    if options.open_result {
        let name = if spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if spec.len() == 1 {
            PackageIdSpec::parse(&spec[0])?
                .name()
                .replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(()),
            }
        };

        // Don't bother locking here as if this is getting deleted there's
        // nothing we can do about it and otherwise if it's getting overwritten
        // then that's also ok!
        let mut target_dir = ws.target_dir();
        if let Some(triple) = options.compile_opts.target {
            target_dir.push(Path::new(triple).file_stem().unwrap());
        }
        let path = target_dir.join("doc").join(&name).join("index.html");
        let path = path.into_path_unlocked();
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            shell.status("Opening", path.display())?;
            match open_docs(&path) {
                Ok(m) => shell.status("Launching", m)?,
                Err(e) => {
                    shell.warn(
                            "warning: could not determine a browser to open docs with, tried:")?;
                    for method in e {
                        shell.warn(format!("\t{}", method))?;
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    if let Ok(name) = env::var("BROWSER") {
        match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER"),
        }
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m),
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg(path).status() {
        Ok(_) => Ok("cmd /C"),
        Err(_) => Err(vec!["cmd /C"]),
    }
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => Ok("open"),
        Err(_) => Err(vec!["open"]),
    }
}
<|endoftext|>


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

use std::fmt;
use std::result;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;

use regex::Regex;

use error::{Error, Result};

lazy_static! {
    static ref FROM_STR_RE: Regex =
        Regex::new(r"\A(?P<service>[^.]+)\.(?P<group>[^.@]+)(@(?P<organization>.+))?\z").unwrap();
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
pub struct ServiceGroup(String);

impl ServiceGroup {
    pub fn new<S1, S2>(service: S1, group: S2, organization: Option<&str>) -> Result<Self>
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        let formatted = Self::format(service, group, organization);
        try!(Self::validate(&formatted));
        Ok(ServiceGroup(formatted))
    }

    pub fn format<S1, S2>(service: S1, group: S2, organization: Option<&str>) -> String
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        if let Some(org) = organization {
            format!("{}.{}@{}", service.as_ref(), group.as_ref(), org)
        } else {
            format!("{}.{}", service.as_ref(), group.as_ref())
        }
    }

    pub fn validate(value: &str) -> Result<()> {
        let caps = FROM_STR_RE
            .captures(value)
            .ok_or(Error::InvalidServiceGroup(value.to_string()))?;
        if caps.name("service").is_none() {
            return Err(Error::InvalidServiceGroup(value.to_string()));
        }
        if caps.name("group").is_none() {
            return Err(Error::InvalidServiceGroup(value.to_string()));
        }
        Ok(())
    }

    pub fn service(&self) -> &str {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("service")
            .unwrap()
            .as_str()
    }

    pub fn group(&self) -> &str {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("group")
            .unwrap()
            .as_str()
    }

    pub fn org(&self) -> Option<&str> {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("organization")
            .and_then(|v| Some(v.as_str()))
    }

    /// Set a new organization for this Service Group.
    ///
    /// This is useful if the organization was lazily loaded or added after creation.
    pub fn set_org<T: AsRef<str>>(&mut self, org: T) {
        self.0 = Self::format(self.service(), self.group(), Some(org.as_ref()));
    }
}

impl AsRef<str> for ServiceGroup {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for ServiceGroup {
    type Target = String;

    fn deref(&self) -> &String {
        &self.0
    }
}

impl DerefMut for ServiceGroup {
    fn deref_mut(&mut self) -> &mut String {
        &mut self.0
    }
}

impl fmt::Display for ServiceGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromStr for ServiceGroup {
    type Err = Error;

    fn from_str(value: &str) -> result::Result<Self, Self::Err> {
        let caps = match FROM_STR_RE.captures(value) {
            Some(c) => c,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let service = match caps.name("service") {
            Some(s) => s.as_str(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let group = match caps.name("group") {
            Some(g) => g.as_str(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let org = match caps.name("organization") {
            Some(o) => Some(o.as_str()),
            None => None,
        };
        Ok(ServiceGroup(ServiceGroup::format(service, group, org)))
    }
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use super::ServiceGroup;

    #[test]
    fn service_groups_with_org() {
        let x = ServiceGroup::from_str("foo.bar").unwrap();
        assert_eq!(x.service(), "foo");
        assert_eq!(x.group(), "bar");
        assert!(x.org().is_none());

        let y = ServiceGroup::from_str("foo.bar@baz").unwrap();
        assert_eq!(y.service(), "foo");
        assert_eq!(y.group(), "bar");
        assert_eq!(y.org().unwrap(), "baz");

        assert!(ServiceGroup::from_str("foo.bar@").is_err());
        assert!(ServiceGroup::from_str("f.oo.bar@baz").is_err());
        assert!(ServiceGroup::from_str("foo@baz").is_err());
    }

    #[test]
    #[should_panic(expected = "not.allowed@")]
    fn from_str_ending_with_at() {
        ServiceGroup::from_str("not.allowed@").unwrap();
    }

    #[test]
    #[should_panic(expected = "only.one.period@allowed")]
    fn from_str_too_many_periods() {
        ServiceGroup::from_str("only.one.period@allowed").unwrap();
    }

    #[test]
    #[should_panic(expected = "oh-noes")]
    fn from_str_not_enough_periods() {
        ServiceGroup::from_str("oh-noes").unwrap();
    }
}
<commit_msg>Remove redundant pub and dead code from components/core/src/service.rs<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::result;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;

use regex::Regex;

use error::{Error, Result};

lazy_static! {
    static ref FROM_STR_RE: Regex =
        Regex::new(r"\A(?P<service>[^.]+)\.(?P<group>[^.@]+)(@(?P<organization>.+))?\z").unwrap();
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
pub struct ServiceGroup(String);

impl ServiceGroup {
    pub fn new<S1, S2>(service: S1, group: S2, organization: Option<&str>) -> Result<Self>
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        let formatted = Self::format(service, group, organization);
        try!(Self::validate(&formatted));
        Ok(ServiceGroup(formatted))
    }

    fn format<S1, S2>(service: S1, group: S2, organization: Option<&str>) -> String
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        if let Some(org) = organization {
            format!("{}.{}@{}", service.as_ref(), group.as_ref(), org)
        } else {
            format!("{}.{}", service.as_ref(), group.as_ref())
        }
    }

    pub fn validate(value: &str) -> Result<()> {
        let caps = FROM_STR_RE
            .captures(value)
            .ok_or(Error::InvalidServiceGroup(value.to_string()))?;
        if caps.name("service").is_none() {
            return Err(Error::InvalidServiceGroup(value.to_string()));
        }
        if caps.name("group").is_none() {
            return Err(Error::InvalidServiceGroup(value.to_string()));
        }
        Ok(())
    }

    pub fn service(&self) -> &str {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("service")
            .unwrap()
            .as_str()
    }

    pub fn group(&self) -> &str {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("group")
            .unwrap()
            .as_str()
    }

    pub fn org(&self) -> Option<&str> {
        FROM_STR_RE
            .captures(&self.0)
            .unwrap()
            .name("organization")
            .and_then(|v| Some(v.as_str()))
    }

    /// Set a new organization for this Service Group.
    ///
    /// This is useful if the organization was lazily loaded or added after creation.
    fn set_org<T: AsRef<str>>(&mut self, org: T) {
        self.0 = Self::format(self.service(), self.group(), Some(org.as_ref()));
    }
}

impl AsRef<str> for ServiceGroup {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for ServiceGroup {
    type Target = String;

    fn deref(&self) -> &String {
        &self.0
    }
}

impl DerefMut for ServiceGroup {
    fn deref_mut(&mut self) -> &mut String {
        &mut self.0
    }
}

impl fmt::Display for ServiceGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl FromStr for ServiceGroup {
    type Err = Error;

    fn from_str(value: &str) -> result::Result<Self, Self::Err> {
        let caps = match FROM_STR_RE.captures(value) {
            Some(c) => c,
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let service = match caps.name("service") {
            Some(s) => s.as_str(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let group = match caps.name("group") {
            Some(g) => g.as_str(),
            None => return Err(Error::InvalidServiceGroup(value.to_string())),
        };
        let org = match caps.name("organization") {
            Some(o) => Some(o.as_str()),
            None => None,
        };
        Ok(ServiceGroup(ServiceGroup::format(service, group, org)))
    }
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use super::ServiceGroup;

    #[test]
    fn service_groups_with_org() {
        let x = ServiceGroup::from_str("foo.bar").unwrap();
        assert_eq!(x.service(), "foo");
        assert_eq!(x.group(), "bar");
        assert!(x.org().is_none());

        let y = ServiceGroup::from_str("foo.bar@baz").unwrap();
        assert_eq!(y.service(), "foo");
        assert_eq!(y.group(), "bar");
        assert_eq!(y.org().unwrap(), "baz");

        assert!(ServiceGroup::from_str("foo.bar@").is_err());
        assert!(ServiceGroup::from_str("f.oo.bar@baz").is_err());
        assert!(ServiceGroup::from_str("foo@baz").is_err());
    }

    #[test]
    #[should_panic(expected = "not.allowed@")]
    fn from_str_ending_with_at() {
        ServiceGroup::from_str("not.allowed@").unwrap();
    }

    #[test]
    #[should_panic(expected = "only.one.period@allowed")]
    fn from_str_too_many_periods() {
        ServiceGroup::from_str("only.one.period@allowed").unwrap();
    }

    #[test]
    #[should_panic(expected = "oh-noes")]
    fn from_str_not_enough_periods() {
        ServiceGroup::from_str("oh-noes").unwrap();
    }
}
<|endoftext|>


<commit_before>use std::iter::once;

use hir::Semantics;
use ide_db::{base_db::FileRange, RootDatabase};
use itertools::Itertools;
use syntax::{
    algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
};
use text_edit::{TextEdit, TextEditBuilder};

pub enum Direction {
    Up,
    Down,
}

// Feature: Move Item
//
// Move item under cursor or selection up and down.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Move item up**
// | VS Code | **Rust Analyzer: Move item down**
// |===
pub(crate) fn move_item(
    db: &RootDatabase,
    range: FileRange,
    direction: Direction,
) -> Option<TextEdit> {
    let sema = Semantics::new(db);
    let file = sema.parse(range.file_id);

    let item = file.syntax().covering_element(range.range);
    find_ancestors(item, direction, range.range)
}

fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
    let root = match item {
        NodeOrToken::Node(node) => node,
        NodeOrToken::Token(token) => token.parent()?,
    };

    let movable = [
        SyntaxKind::ARG_LIST,
        SyntaxKind::GENERIC_PARAM_LIST,
        SyntaxKind::GENERIC_ARG_LIST,
        SyntaxKind::VARIANT_LIST,
        SyntaxKind::TYPE_BOUND_LIST,
        SyntaxKind::MATCH_ARM,
        SyntaxKind::PARAM,
        SyntaxKind::LET_STMT,
        SyntaxKind::EXPR_STMT,
        SyntaxKind::MATCH_EXPR,
        SyntaxKind::MACRO_CALL,
        SyntaxKind::TYPE_ALIAS,
        SyntaxKind::TRAIT,
        SyntaxKind::IMPL,
        SyntaxKind::MACRO_DEF,
        SyntaxKind::STRUCT,
        SyntaxKind::UNION,
        SyntaxKind::ENUM,
        SyntaxKind::FN,
        SyntaxKind::MODULE,
        SyntaxKind::USE,
        SyntaxKind::STATIC,
        SyntaxKind::CONST,
        SyntaxKind::MACRO_RULES,
        SyntaxKind::MACRO_DEF,
    ];

    let ancestor = once(root.clone())
        .chain(root.ancestors())
        .find(|ancestor| movable.contains(&ancestor.kind()))?;

    move_in_direction(&ancestor, direction, range)
}

fn move_in_direction(
    node: &SyntaxNode,
    direction: Direction,
    range: TextRange,
) -> Option<TextEdit> {
    match_ast! {
        match node {
            ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction),
            ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction),
            ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction),
            ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction),
            ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction),
            _ => Some(replace_nodes(node, &match direction {
                Direction::Up => node.prev_sibling(),
                Direction::Down => node.next_sibling(),
            }?))
        }
    }
}

fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
    list: I,
    range: TextRange,
    direction: Direction,
) -> Option<TextEdit> {
    let (l, r) = list
        .tuple_windows()
        .filter(|(l, r)| match direction {
            Direction::Up => r.syntax().text_range().contains_range(range),
            Direction::Down => l.syntax().text_range().contains_range(range),
        })
        .next()?;

    Some(replace_nodes(l.syntax(), r.syntax()))
}

fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
    let mut edit = TextEditBuilder::default();

    algo::diff(first, second).into_text_edit(&mut edit);
    algo::diff(second, first).into_text_edit(&mut edit);

    edit.finish()
}

#[cfg(test)]
mod tests {
    use crate::fixture;
    use expect_test::{expect, Expect};

    use crate::Direction;

    fn check(ra_fixture: &str, expect: Expect, direction: Direction) {
        let (analysis, range) = fixture::range(ra_fixture);
        let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default();
        let mut file = analysis.file_text(range.file_id).unwrap().to_string();
        edit.apply(&mut file);
        expect.assert_eq(&file);
    }

    #[test]
    fn test_moves_match_arm_up() {
        check(
            r#"
fn main() {
    match true {
        true => {
            println!("Hello, world");
        },
        false =>$0$0 {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        false => {
            println!("Test");
        },
        true => {
            println!("Hello, world");
        }
    };
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_match_arm_down() {
        check(
            r#"
fn main() {
    match true {
        true =>$0$0 {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        false => {
            println!("Test");
        },
        true => {
            println!("Hello, world");
        }
    };
}
            "#]],
            Direction::Down,
        );
    }

    #[test]
    fn test_nowhere_to_move() {
        check(
            r#"
fn main() {
    match true {
        true =>$0$0 {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        true => {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_let_stmt_up() {
        check(
            r#"
fn main() {
    let test = 123;
    let test2$0$0 = 456;
}
            "#,
            expect![[r#"
fn main() {
    let test2 = 456;
    let test = 123;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_expr_up() {
        check(
            r#"
fn main() {
    println!("Hello, world");
    println!("All I want to say is...");$0$0
}
            "#,
            expect![[r#"
fn main() {
    println!("All I want to say is...");
    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_nowhere_to_move_stmt() {
        check(
            r#"
fn main() {
    println!("All I want to say is...");$0$0
    println!("Hello, world");
}
            "#,
            expect![[r#"
fn main() {
    println!("All I want to say is...");
    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_item() {
        check(
            r#"
fn main() {}

fn foo() {}$0$0
            "#,
            expect![[r#"
fn foo() {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_impl_up() {
        check(
            r#"
struct Yay;

trait Wow {}

impl Wow for Yay $0$0{}
            "#,
            expect![[r#"
struct Yay;

impl Wow for Yay {}

trait Wow {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_use_up() {
        check(
            r#"
use std::vec::Vec;
use std::collections::HashMap$0$0;
            "#,
            expect![[r#"
use std::collections::HashMap;
use std::vec::Vec;
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_match_expr_up() {
        check(
            r#"
fn main() {
    let test = 123;

    $0match test {
        456 => {},
        _ => {}
    };$0
}
            "#,
            expect![[r#"
fn main() {
    match test {
        456 => {},
        _ => {}
    };

    let test = 123;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_param_up() {
        check(
            r#"
fn test(one: i32, two$0$0: u32) {}

fn main() {
    test(123, 456);
}
            "#,
            expect![[r#"
fn test(two: u32, one: i32) {}

fn main() {
    test(123, 456);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_arg_up() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123, 456$0$0);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(456, 123);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_arg_down() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123$0$0, 456);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(456, 123);
}
            "#]],
            Direction::Down,
        );
    }

    #[test]
    fn test_nowhere_to_move_arg() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123$0$0, 456);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123, 456);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_generic_param_up() {
        check(
            r#"
struct Test<A, B$0$0>(A, B);

fn main() {}
            "#,
            expect![[r#"
struct Test<B, A>(A, B);

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_generic_arg_up() {
        check(
            r#"
struct Test<A, B>(A, B);

fn main() {
    let t = Test::<i32, &str$0$0>(123, "yay");
}
            "#,
            expect![[r#"
struct Test<A, B>(A, B);

fn main() {
    let t = Test::<&str, i32>(123, "yay");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_variant_up() {
        check(
            r#"
enum Hello {
    One,
    Two$0$0
}

fn main() {}
            "#,
            expect![[r#"
enum Hello {
    Two,
    One
}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_type_bound_up() {
        check(
            r#"
trait One {}

trait Two {}

fn test<T: One + Two$0$0>(t: T) {}

fn main() {}
            "#,
            expect![[r#"
trait One {}

trait Two {}

fn test<T: Two + One>(t: T) {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_prioritizes_trait_items() {
        check(
            r#"
struct Test;

trait Yay {
    type One;

    type Two;

    fn inner();
}

impl Yay for Test {
    type One = i32;

    type Two = u32;

    fn inner() {$0$0
        println!("Mmmm");
    }
}
            "#,
            expect![[r#"
struct Test;

trait Yay {
    type One;

    type Two;

    fn inner();
}

impl Yay for Test {
    type One = i32;

    fn inner() {
        println!("Mmmm");
    }

    type Two = u32;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_weird_nesting() {
        check(
            r#"
fn test() {
    mod hello {
        fn inner() {}
    }

    mod hi {$0$0
        fn inner() {}
    }
}
            "#,
            expect![[r#"
fn test() {
    mod hi {
        fn inner() {}
    }

    mod hello {
        fn inner() {}
    }
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn handles_empty_file() {
        check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
    }
}
<commit_msg>Improved node lookup algorithm, added more movable nodes<commit_after>use std::iter::once;

use hir::Semantics;
use ide_db::{base_db::FileRange, RootDatabase};
use itertools::Itertools;
use syntax::{
    algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
    TokenAtOffset,
};
use text_edit::{TextEdit, TextEditBuilder};

#[derive(Copy, Clone, Debug)]
pub enum Direction {
    Up,
    Down,
}

// Feature: Move Item
//
// Move item under cursor or selection up and down.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Rust Analyzer: Move item up**
// | VS Code | **Rust Analyzer: Move item down**
// |===
pub(crate) fn move_item(
    db: &RootDatabase,
    range: FileRange,
    direction: Direction,
) -> Option<TextEdit> {
    let sema = Semantics::new(db);
    let file = sema.parse(range.file_id);

    let item = if range.range.is_empty() {
        SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?)
    } else {
        file.syntax().covering_element(range.range)
    };

    find_ancestors(item, direction, range.range)
}

fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
    let root = match item {
        SyntaxElement::Node(node) => node,
        SyntaxElement::Token(token) => token.parent()?,
    };

    let movable = [
        SyntaxKind::ARG_LIST,
        SyntaxKind::GENERIC_PARAM_LIST,
        SyntaxKind::GENERIC_ARG_LIST,
        SyntaxKind::VARIANT_LIST,
        SyntaxKind::TYPE_BOUND_LIST,
        SyntaxKind::MATCH_ARM,
        SyntaxKind::PARAM,
        SyntaxKind::LET_STMT,
        SyntaxKind::EXPR_STMT,
        SyntaxKind::IF_EXPR,
        SyntaxKind::FOR_EXPR,
        SyntaxKind::LOOP_EXPR,
        SyntaxKind::WHILE_EXPR,
        SyntaxKind::RETURN_EXPR,
        SyntaxKind::MATCH_EXPR,
        SyntaxKind::MACRO_CALL,
        SyntaxKind::TYPE_ALIAS,
        SyntaxKind::TRAIT,
        SyntaxKind::IMPL,
        SyntaxKind::MACRO_DEF,
        SyntaxKind::STRUCT,
        SyntaxKind::UNION,
        SyntaxKind::ENUM,
        SyntaxKind::FN,
        SyntaxKind::MODULE,
        SyntaxKind::USE,
        SyntaxKind::STATIC,
        SyntaxKind::CONST,
        SyntaxKind::MACRO_RULES,
        SyntaxKind::MACRO_DEF,
    ];

    let ancestor = once(root.clone())
        .chain(root.ancestors())
        .find(|ancestor| movable.contains(&ancestor.kind()))?;

    move_in_direction(&ancestor, direction, range)
}

fn move_in_direction(
    node: &SyntaxNode,
    direction: Direction,
    range: TextRange,
) -> Option<TextEdit> {
    match_ast! {
        match node {
            ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
            ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
            ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
            ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
            ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
            _ => Some(replace_nodes(node, &match direction {
                Direction::Up => node.prev_sibling(),
                Direction::Down => node.next_sibling(),
            }?))
        }
    }
}

fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
    node: &SyntaxNode,
    list: I,
    range: TextRange,
    direction: Direction,
) -> Option<TextEdit> {
    let list_lookup = list
        .tuple_windows()
        .filter(|(l, r)| match direction {
            Direction::Up => r.syntax().text_range().contains_range(range),
            Direction::Down => l.syntax().text_range().contains_range(range),
        })
        .next();

    if let Some((l, r)) = list_lookup {
        Some(replace_nodes(l.syntax(), r.syntax()))
    } else {
        // Cursor is beyond any movable list item (for example, on curly brace in enum).
        // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
        // and we have to continue tree traversal to find suitable node.
        find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
    }
}

fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
    let mut edit = TextEditBuilder::default();

    algo::diff(first, second).into_text_edit(&mut edit);
    algo::diff(second, first).into_text_edit(&mut edit);

    edit.finish()
}

fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
    return tokens.max_by_key(priority);
    fn priority(n: &SyntaxToken) -> usize {
        match n.kind() {
            SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
            kind if kind.is_trivia() => 0,
            _ => 1,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::fixture;
    use expect_test::{expect, Expect};

    use crate::Direction;

    fn check(ra_fixture: &str, expect: Expect, direction: Direction) {
        let (analysis, range) = fixture::range(ra_fixture);
        let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default();
        let mut file = analysis.file_text(range.file_id).unwrap().to_string();
        edit.apply(&mut file);
        expect.assert_eq(&file);
    }

    #[test]
    fn test_moves_match_arm_up() {
        check(
            r#"
fn main() {
    match true {
        true => {
            println!("Hello, world");
        },
        false =>$0$0 {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        false => {
            println!("Test");
        },
        true => {
            println!("Hello, world");
        }
    };
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_match_arm_down() {
        check(
            r#"
fn main() {
    match true {
        true =>$0$0 {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        false => {
            println!("Test");
        },
        true => {
            println!("Hello, world");
        }
    };
}
            "#]],
            Direction::Down,
        );
    }

    #[test]
    fn test_nowhere_to_move() {
        check(
            r#"
fn main() {
    match true {
        true =>$0$0 {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#,
            expect![[r#"
fn main() {
    match true {
        true => {
            println!("Hello, world");
        },
        false => {
            println!("Test");
        }
    };
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_let_stmt_up() {
        check(
            r#"
fn main() {
    let test = 123;
    let test2$0$0 = 456;
}
            "#,
            expect![[r#"
fn main() {
    let test2 = 456;
    let test = 123;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_expr_up() {
        check(
            r#"
fn main() {
    println!("Hello, world");
    println!("All I want to say is...");$0$0
}
            "#,
            expect![[r#"
fn main() {
    println!("All I want to say is...");
    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
        check(
            r#"
fn main() {
    println!("Hello, world");

    if true {
        println!("Test");
    }$0$0
}
            "#,
            expect![[r#"
fn main() {
    if true {
        println!("Test");
    }

    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
        check(
            r#"
fn main() {
    println!("Hello, world");

    for i in 0..10 {
        println!("Test");
    }$0$0
}
            "#,
            expect![[r#"
fn main() {
    for i in 0..10 {
        println!("Test");
    }

    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
        check(
            r#"
fn main() {
    println!("Hello, world");

    loop {
        println!("Test");
    }$0$0
}
            "#,
            expect![[r#"
fn main() {
    loop {
        println!("Test");
    }

    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
        check(
            r#"
fn main() {
    println!("Hello, world");

    while true {
        println!("Test");
    }$0$0
}
            "#,
            expect![[r#"
fn main() {
    while true {
        println!("Test");
    }

    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
        check(
            r#"
fn main() {
    println!("Hello, world");

    return 123;$0$0
}
            "#,
            expect![[r#"
fn main() {
    return 123;

    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_nowhere_to_move_stmt() {
        check(
            r#"
fn main() {
    println!("All I want to say is...");$0$0
    println!("Hello, world");
}
            "#,
            expect![[r#"
fn main() {
    println!("All I want to say is...");
    println!("Hello, world");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_item() {
        check(
            r#"
fn main() {}

fn foo() {}$0$0
            "#,
            expect![[r#"
fn foo() {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_impl_up() {
        check(
            r#"
struct Yay;

trait Wow {}

impl Wow for Yay $0$0{}
            "#,
            expect![[r#"
struct Yay;

impl Wow for Yay {}

trait Wow {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_move_use_up() {
        check(
            r#"
use std::vec::Vec;
use std::collections::HashMap$0$0;
            "#,
            expect![[r#"
use std::collections::HashMap;
use std::vec::Vec;
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_match_expr_up() {
        check(
            r#"
fn main() {
    let test = 123;

    $0match test {
        456 => {},
        _ => {}
    };$0
}
            "#,
            expect![[r#"
fn main() {
    match test {
        456 => {},
        _ => {}
    };

    let test = 123;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_param_up() {
        check(
            r#"
fn test(one: i32, two$0$0: u32) {}

fn main() {
    test(123, 456);
}
            "#,
            expect![[r#"
fn test(two: u32, one: i32) {}

fn main() {
    test(123, 456);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_arg_up() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123, 456$0$0);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(456, 123);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_arg_down() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123$0$0, 456);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(456, 123);
}
            "#]],
            Direction::Down,
        );
    }

    #[test]
    fn test_nowhere_to_move_arg() {
        check(
            r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123$0$0, 456);
}
            "#,
            expect![[r#"
fn test(one: i32, two: u32) {}

fn main() {
    test(123, 456);
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_generic_param_up() {
        check(
            r#"
struct Test<A, B$0$0>(A, B);

fn main() {}
            "#,
            expect![[r#"
struct Test<B, A>(A, B);

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_generic_arg_up() {
        check(
            r#"
struct Test<A, B>(A, B);

fn main() {
    let t = Test::<i32, &str$0$0>(123, "yay");
}
            "#,
            expect![[r#"
struct Test<A, B>(A, B);

fn main() {
    let t = Test::<&str, i32>(123, "yay");
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_variant_up() {
        check(
            r#"
enum Hello {
    One,
    Two$0$0
}

fn main() {}
            "#,
            expect![[r#"
enum Hello {
    Two,
    One
}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_moves_type_bound_up() {
        check(
            r#"
trait One {}

trait Two {}

fn test<T: One + Two$0$0>(t: T) {}

fn main() {}
            "#,
            expect![[r#"
trait One {}

trait Two {}

fn test<T: Two + One>(t: T) {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_prioritizes_trait_items() {
        check(
            r#"
struct Test;

trait Yay {
    type One;

    type Two;

    fn inner();
}

impl Yay for Test {
    type One = i32;

    type Two = u32;

    fn inner() {$0$0
        println!("Mmmm");
    }
}
            "#,
            expect![[r#"
struct Test;

trait Yay {
    type One;

    type Two;

    fn inner();
}

impl Yay for Test {
    type One = i32;

    fn inner() {
        println!("Mmmm");
    }

    type Two = u32;
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_weird_nesting() {
        check(
            r#"
fn test() {
    mod hello {
        fn inner() {}
    }

    mod hi {$0$0
        fn inner() {}
    }
}
            "#,
            expect![[r#"
fn test() {
    mod hi {
        fn inner() {}
    }

    mod hello {
        fn inner() {}
    }
}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_cursor_at_item_start() {
        check(
            r#"
$0$0#[derive(Debug)]
enum FooBar {
    Foo,
    Bar,
}

fn main() {}
            "#,
            expect![[r#"
fn main() {}

#[derive(Debug)]
enum FooBar {
    Foo,
    Bar,
}
            "#]],
            Direction::Down,
        );
        check(
            r#"
$0$0enum FooBar {
    Foo,
    Bar,
}

fn main() {}
            "#,
            expect![[r#"
fn main() {}

enum FooBar {
    Foo,
    Bar,
}
            "#]],
            Direction::Down,
        );
        check(
            r#"
struct Test;

trait SomeTrait {}

$0$0impl SomeTrait for Test {}

fn main() {}
            "#,
            expect![[r#"
struct Test;

impl SomeTrait for Test {}

trait SomeTrait {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn test_cursor_at_item_end() {
        check(
            r#"
enum FooBar {
    Foo,
    Bar,
}$0$0

fn main() {}
            "#,
            expect![[r#"
fn main() {}

enum FooBar {
    Foo,
    Bar,
}
            "#]],
            Direction::Down,
        );
        check(
            r#"
struct Test;

trait SomeTrait {}

impl SomeTrait for Test {}$0$0

fn main() {}
            "#,
            expect![[r#"
struct Test;

impl SomeTrait for Test {}

trait SomeTrait {}

fn main() {}
            "#]],
            Direction::Up,
        );
    }

    #[test]
    fn handles_empty_file() {
        check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
    }
}
<|endoftext|>