<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Debug callback called by intermediate layers or by the driver.
//!
//! When working on an application, it is recommended to register a debug callback. For example if
//! you enable the validation layers provided by the official Vulkan SDK, they will warn you about
//! invalid API usages or performance problems by calling this callback. The callback can also
//! be called by the driver or by whatever intermediate layer is activated.
//!
//! Note that the vulkano library can also emit messages to warn you about performance issues.
//! TODO: ^ that's not the case yet, need to choose whether we keep this idea
//!
//! # Example
//!
//! ```
//! # use vulkano::instance::Instance;
//! # use std::sync::Arc;
//! # let instance: Arc<Instance> = return;
//! use vulkano::instance::debug::DebugCallback;
//!
//! let _callback = DebugCallback::errors_and_warnings(&instance, |msg| {
//!     println!("Debug callback: {:?}", msg.description);
//! }).ok();
//! ```
//!
//! The type of `msg` in the callback is [`Message`](struct.Message.html).
//!
//! Note that you must keep the `_callback` object alive for as long as you want your callback to
//! be callable. If you don't store the return value of `DebugCallback`'s constructor in a
//! variable, it will be immediately destroyed and your callback will not work.
//!

use crate::check_errors;
use crate::instance::Instance;
use crate::Error;
use crate::VulkanObject;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic;
use std::ptr;
use std::sync::Arc;

/// Registration of a callback called by validation layers.
///
/// The callback can be called as long as this object is alive.
#[must_use = "The DebugCallback object must be kept alive for as long as you want your callback \
              to be called"]
pub struct DebugCallback {
    instance: Arc<Instance>,
    debug_report_callback: ash::vk::DebugUtilsMessengerEXT,
    user_callback: Box<Box<dyn Fn(&Message) + Send>>,
}

impl DebugCallback {
    /// Initializes a debug callback.
    ///
    /// Panics generated by calling `user_callback` are ignored.
    pub fn new<F>(
        instance: &Arc<Instance>,
        severity: MessageSeverity,
        ty: MessageType,
        user_callback: F,
    ) -> Result<DebugCallback, DebugCallbackCreationError>
    where
        F: Fn(&Message) + 'static + Send + panic::RefUnwindSafe,
    {
        if !instance.enabled_extensions().ext_debug_utils {
            return Err(DebugCallbackCreationError::MissingExtension);
        }

        // Note that we need to double-box the callback, because a `*const Fn()` is a fat pointer
        // that can't be cast to a `*const c_void`.
        let user_callback = Box::new(Box::new(user_callback) as Box<_>);

        unsafe extern "system" fn callback(
            severity: ash::vk::DebugUtilsMessageSeverityFlagsEXT,
            ty: ash::vk::DebugUtilsMessageTypeFlagsEXT,
            callback_data: *const ash::vk::DebugUtilsMessengerCallbackDataEXT,
            user_data: *mut c_void,
        ) -> ash::vk::Bool32 {
            let user_callback = user_data as *mut Box<dyn Fn()> as *const _;
            let user_callback: &Box<dyn Fn(&Message)> = &*user_callback;

            let layer_prefix = (*callback_data)
                .p_message_id_name
                .as_ref()
                .map(|msg_id_name| {
                    CStr::from_ptr(msg_id_name)
                        .to_str()
                        .expect("debug callback message not utf-8")
                });

            let description = CStr::from_ptr((*callback_data).p_message)
                .to_str()
                .expect("debug callback message not utf-8");

            let message = Message {
                severity: MessageSeverity {
                    information: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO)
                        .is_empty(),
                    warning: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING)
                        .is_empty(),
                    error: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR)
                        .is_empty(),
                    verbose: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE)
                        .is_empty(),
                },
                ty: MessageType {
                    general: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL).is_empty(),
                    validation: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION)
                        .is_empty(),
                    performance: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE)
                        .is_empty(),
                },
                layer_prefix,
                description,
            };

            // Since we box the closure, the type system doesn't detect that the `UnwindSafe`
            // bound is enforced. Therefore we enforce it manually.
            let _ = panic::catch_unwind(panic::AssertUnwindSafe(move || {
                user_callback(&message);
            }));

            ash::vk::FALSE
        }

        let severity = {
            let mut flags = ash::vk::DebugUtilsMessageSeverityFlagsEXT::empty();
            if severity.information {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
            }
            if severity.warning {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
            }
            if severity.error {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
            }
            if severity.verbose {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
            }
            flags
        };

        let ty = {
            let mut flags = ash::vk::DebugUtilsMessageTypeFlagsEXT::empty();
            if ty.general {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL;
            }
            if ty.validation {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION;
            }
            if ty.performance {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
            }
            flags
        };

        let infos = ash::vk::DebugUtilsMessengerCreateInfoEXT {
            flags: ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
            message_severity: severity,
            message_type: ty,
            pfn_user_callback: Some(callback),
            p_user_data: &*user_callback as &Box<_> as *const Box<_> as *const c_void as *mut _,
            ..Default::default()
        };

        let fns = instance.fns();

        let debug_report_callback = unsafe {
            let mut output = MaybeUninit::uninit();
            check_errors(fns.ext_debug_utils.create_debug_utils_messenger_ext(
                instance.internal_object(),
                &infos,
                ptr::null(),
                output.as_mut_ptr(),
            ))?;
            output.assume_init()
        };

        Ok(DebugCallback {
            instance: instance.clone(),
            debug_report_callback,
            user_callback,
        })
    }

    /// Initializes a debug callback with errors and warnings.
    ///
    /// Shortcut for `new(instance, MessageTypes::errors_and_warnings(), user_callback)`.
    #[inline]
    pub fn errors_and_warnings<F>(
        instance: &Arc<Instance>,
        user_callback: F,
    ) -> Result<DebugCallback, DebugCallbackCreationError>
    where
        F: Fn(&Message) + Send + 'static + panic::RefUnwindSafe,
    {
        DebugCallback::new(
            instance,
            MessageSeverity::errors_and_warnings(),
            MessageType::general(),
            user_callback,
        )
    }
}

impl Drop for DebugCallback {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let fns = self.instance.fns();
            fns.ext_debug_utils.destroy_debug_utils_messenger_ext(
                self.instance.internal_object(),
                self.debug_report_callback,
                ptr::null(),
            );
        }
    }
}

/// A message received by the callback.
pub struct Message<'a> {
    /// Severity of message.
    pub severity: MessageSeverity,
    /// Type of message,
    pub ty: MessageType,
    /// Prefix of the layer that reported this message or `None` if unknown.
    pub layer_prefix: Option<&'a str>,
    /// Description of the message.
    pub description: &'a str,
}

/// Severity of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageSeverity {
    /// An error that may cause undefined results, including an application crash.
    pub error: bool,
    /// An unexpected use.
    pub warning: bool,
    /// An informational message that may be handy when debugging an application.
    pub information: bool,
    /// Diagnostic information from the loader and layers.
    pub verbose: bool,
}

impl MessageSeverity {
    /// Builds a `MessageSeverity` with all fields set to `false` expect `error`.
    #[inline]
    pub fn errors() -> MessageSeverity {
        MessageSeverity {
            error: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false` expect `error`, `warning`
    /// and `performance_warning`.
    #[inline]
    pub fn errors_and_warnings() -> MessageSeverity {
        MessageSeverity {
            error: true,
            warning: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false`.
    #[inline]
    pub fn none() -> MessageSeverity {
        MessageSeverity {
            error: false,
            warning: false,
            information: false,
            verbose: false,
        }
    }
}

/// Type of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageType {
    /// Specifies that some general event has occurred.
    pub general: bool,
    /// Specifies that something has occurred during validation against the vulkan specification
    pub validation: bool,
    /// Specifies a potentially non-optimal use of Vulkan
    pub performance: bool,
}

impl MessageType {
    /// Builds a `MessageType` with general field set to `true`.
    #[inline]
    pub fn general() -> MessageType {
        MessageType {
            general: true,
            validation: false,
            performance: false,
        }
    }

    /// Builds a `MessageType` with all fields set to `true`.
    #[inline]
    pub fn all() -> MessageType {
        MessageType {
            general: true,
            validation: true,
            performance: true,
        }
    }

    /// Builds a `MessageType` with all fields set to `false`.
    #[inline]
    pub fn none() -> MessageType {
        MessageType {
            general: false,
            validation: false,
            performance: false,
        }
    }
}

/// Error that can happen when creating a debug callback.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DebugCallbackCreationError {
    /// The `EXT_debug_report` extension was not enabled.
    MissingExtension,
}

impl error::Error for DebugCallbackCreationError {}

impl fmt::Display for DebugCallbackCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                DebugCallbackCreationError::MissingExtension => {
                    "the `EXT_debug_report` extension was not enabled"
                }
            }
        )
    }
}

impl From<Error> for DebugCallbackCreationError {
    #[inline]
    fn from(err: Error) -> DebugCallbackCreationError {
        panic!("unexpected error: {:?}", err)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    #[test]
    fn ensure_sendable() {
        // It's useful to be able to initialize a DebugCallback on one thread
        // and keep it alive on another thread.
        let instance = instance!();
        let severity = MessageSeverity::none();
        let ty = MessageType::all();
        let callback = DebugCallback::new(&instance, severity, ty, |_| {});
        thread::spawn(move || {
            let _ = callback;
        });
    }
}
<commit_msg>improve the debug interface a bit and update the error message (#1660)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Debug callback called by intermediate layers or by the driver.
//!
//! When working on an application, it is recommended to register a debug callback. For example if
//! you enable the validation layers provided by the official Vulkan SDK, they will warn you about
//! invalid API usages or performance problems by calling this callback. The callback can also
//! be called by the driver or by whatever intermediate layer is activated.
//!
//! Note that the vulkano library can also emit messages to warn you about performance issues.
//! TODO: ^ that's not the case yet, need to choose whether we keep this idea
//!
//! # Example
//!
//! ```
//! # use vulkano::instance::Instance;
//! # use std::sync::Arc;
//! # let instance: Arc<Instance> = return;
//! use vulkano::instance::debug::DebugCallback;
//!
//! let _callback = DebugCallback::errors_and_warnings(&instance, |msg| {
//!     println!("Debug callback: {:?}", msg.description);
//! }).ok();
//! ```
//!
//! The type of `msg` in the callback is [`Message`](struct.Message.html).
//!
//! Note that you must keep the `_callback` object alive for as long as you want your callback to
//! be callable. If you don't store the return value of `DebugCallback`'s constructor in a
//! variable, it will be immediately destroyed and your callback will not work.
//!

use crate::check_errors;
use crate::instance::Instance;
use crate::Error;
use crate::VulkanObject;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic;
use std::ptr;
use std::sync::Arc;

/// Registration of a callback called by validation layers.
///
/// The callback can be called as long as this object is alive.
#[must_use = "The DebugCallback object must be kept alive for as long as you want your callback \
              to be called"]
pub struct DebugCallback {
    instance: Arc<Instance>,
    debug_report_callback: ash::vk::DebugUtilsMessengerEXT,
    user_callback: Box<Box<dyn Fn(&Message) + Send>>,
}

impl DebugCallback {
    /// Initializes a debug callback.
    ///
    /// Panics generated by calling `user_callback` are ignored.
    pub fn new<F>(
        instance: &Arc<Instance>,
        severity: MessageSeverity,
        ty: MessageType,
        user_callback: F,
    ) -> Result<DebugCallback, DebugCallbackCreationError>
    where
        F: Fn(&Message) + 'static + Send + panic::RefUnwindSafe,
    {
        if !instance.enabled_extensions().ext_debug_utils {
            return Err(DebugCallbackCreationError::MissingExtension);
        }

        // Note that we need to double-box the callback, because a `*const Fn()` is a fat pointer
        // that can't be cast to a `*const c_void`.
        let user_callback = Box::new(Box::new(user_callback) as Box<_>);

        unsafe extern "system" fn callback(
            severity: ash::vk::DebugUtilsMessageSeverityFlagsEXT,
            ty: ash::vk::DebugUtilsMessageTypeFlagsEXT,
            callback_data: *const ash::vk::DebugUtilsMessengerCallbackDataEXT,
            user_data: *mut c_void,
        ) -> ash::vk::Bool32 {
            let user_callback = user_data as *mut Box<dyn Fn()> as *const _;
            let user_callback: &Box<dyn Fn(&Message)> = &*user_callback;

            let layer_prefix = (*callback_data)
                .p_message_id_name
                .as_ref()
                .map(|msg_id_name| {
                    CStr::from_ptr(msg_id_name)
                        .to_str()
                        .expect("debug callback message not utf-8")
                });

            let description = CStr::from_ptr((*callback_data).p_message)
                .to_str()
                .expect("debug callback message not utf-8");

            let message = Message {
                severity: MessageSeverity {
                    information: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO)
                        .is_empty(),
                    warning: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING)
                        .is_empty(),
                    error: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR)
                        .is_empty(),
                    verbose: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE)
                        .is_empty(),
                },
                ty: MessageType {
                    general: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL).is_empty(),
                    validation: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION)
                        .is_empty(),
                    performance: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE)
                        .is_empty(),
                },
                layer_prefix,
                description,
            };

            // Since we box the closure, the type system doesn't detect that the `UnwindSafe`
            // bound is enforced. Therefore we enforce it manually.
            let _ = panic::catch_unwind(panic::AssertUnwindSafe(move || {
                user_callback(&message);
            }));

            ash::vk::FALSE
        }

        let severity = {
            let mut flags = ash::vk::DebugUtilsMessageSeverityFlagsEXT::empty();
            if severity.information {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
            }
            if severity.warning {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
            }
            if severity.error {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
            }
            if severity.verbose {
                flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
            }
            flags
        };

        let ty = {
            let mut flags = ash::vk::DebugUtilsMessageTypeFlagsEXT::empty();
            if ty.general {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL;
            }
            if ty.validation {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION;
            }
            if ty.performance {
                flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
            }
            flags
        };

        let infos = ash::vk::DebugUtilsMessengerCreateInfoEXT {
            flags: ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
            message_severity: severity,
            message_type: ty,
            pfn_user_callback: Some(callback),
            p_user_data: &*user_callback as &Box<_> as *const Box<_> as *const c_void as *mut _,
            ..Default::default()
        };

        let fns = instance.fns();

        let debug_report_callback = unsafe {
            let mut output = MaybeUninit::uninit();
            check_errors(fns.ext_debug_utils.create_debug_utils_messenger_ext(
                instance.internal_object(),
                &infos,
                ptr::null(),
                output.as_mut_ptr(),
            ))?;
            output.assume_init()
        };

        Ok(DebugCallback {
            instance: instance.clone(),
            debug_report_callback,
            user_callback,
        })
    }

    /// Initializes a debug callback with errors and warnings.
    ///
    /// Shortcut for `new(instance, MessageTypes::errors_and_warnings(), user_callback)`.
    #[inline]
    pub fn errors_and_warnings<F>(
        instance: &Arc<Instance>,
        user_callback: F,
    ) -> Result<DebugCallback, DebugCallbackCreationError>
    where
        F: Fn(&Message) + Send + 'static + panic::RefUnwindSafe,
    {
        DebugCallback::new(
            instance,
            MessageSeverity::errors_and_warnings(),
            MessageType::general(),
            user_callback,
        )
    }
}

impl Drop for DebugCallback {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let fns = self.instance.fns();
            fns.ext_debug_utils.destroy_debug_utils_messenger_ext(
                self.instance.internal_object(),
                self.debug_report_callback,
                ptr::null(),
            );
        }
    }
}

/// A message received by the callback.
pub struct Message<'a> {
    /// Severity of message.
    pub severity: MessageSeverity,
    /// Type of message,
    pub ty: MessageType,
    /// Prefix of the layer that reported this message or `None` if unknown.
    pub layer_prefix: Option<&'a str>,
    /// Description of the message.
    pub description: &'a str,
}

/// Severity of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageSeverity {
    /// An error that may cause undefined results, including an application crash.
    pub error: bool,
    /// An unexpected use.
    pub warning: bool,
    /// An informational message that may be handy when debugging an application.
    pub information: bool,
    /// Diagnostic information from the loader and layers.
    pub verbose: bool,
}

impl MessageSeverity {
    /// Builds a `MessageSeverity` with all fields set to `false` expect `error`.
    #[inline]
    pub const fn errors() -> MessageSeverity {
        MessageSeverity {
            error: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false` expect `warning`.
    #[inline]
    pub const fn warnings() -> MessageSeverity {
        MessageSeverity {
            warning: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false` expect `information`.
    #[inline]
    pub const fn information() -> MessageSeverity {
        MessageSeverity {
            information: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false` expect `verbose`.
    #[inline]
    pub const fn verbose() -> MessageSeverity {
        MessageSeverity {
            verbose: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false` expect `error`, `warning`
    /// and `performance_warning`.
    #[inline]
    pub const fn errors_and_warnings() -> MessageSeverity {
        MessageSeverity {
            error: true,
            warning: true,
            ..MessageSeverity::none()
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `false`.
    #[inline]
    pub const fn none() -> MessageSeverity {
        MessageSeverity {
            error: false,
            warning: false,
            information: false,
            verbose: false,
        }
    }

    /// Builds a `MessageSeverity` with all fields set to `true`.
    #[inline]
    pub const fn all() -> MessageSeverity {
        MessageSeverity {
            error: true,
            warning: true,
            information: true,
            verbose: true,
        }
    }
}

impl std::ops::BitOr for MessageSeverity {
    type Output = Self;
    fn bitor(self, rhs: Self) -> Self::Output {
        MessageSeverity {
            error: self.error | rhs.error,
            warning: self.warning | rhs.warning,
            information: self.information | rhs.information,
            verbose: self.verbose | rhs.verbose,
        }
    }
}

/// Type of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageType {
    /// Specifies that some general event has occurred.
    pub general: bool,
    /// Specifies that something has occurred during validation against the vulkan specification
    pub validation: bool,
    /// Specifies a potentially non-optimal use of Vulkan
    pub performance: bool,
}

impl MessageType {
    /// Builds a `MessageType` with general field set to `true`.
    #[inline]
    pub const fn general() -> MessageType {
        MessageType {
            general: true,
            validation: false,
            performance: false,
        }
    }

    /// Builds a `MessageType` with validation field set to `true`.
    #[inline]
    pub const fn validation() -> MessageType {
        MessageType {
            general: false,
            validation: true,
            performance: false,
        }
    }

    /// Builds a `MessageType` with performance field set to `true`.
    #[inline]
    pub const fn performance() -> MessageType {
        MessageType {
            general: false,
            validation: false,
            performance: true,
        }
    }

    /// Builds a `MessageType` with all fields set to `true`.
    #[inline]
    pub const fn all() -> MessageType {
        MessageType {
            general: true,
            validation: true,
            performance: true,
        }
    }

    /// Builds a `MessageType` with all fields set to `false`.
    #[inline]
    pub const fn none() -> MessageType {
        MessageType {
            general: false,
            validation: false,
            performance: false,
        }
    }
}

impl std::ops::BitOr for MessageType {
    type Output = Self;
    fn bitor(self, rhs: Self) -> Self::Output {
        MessageType {
            general: self.general | rhs.general,
            validation: self.validation | rhs.validation,
            performance: self.performance | rhs.performance,
        }
    }
}

/// Error that can happen when creating a debug callback.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DebugCallbackCreationError {
    /// The `EXT_debug_utils` extension was not enabled.
    MissingExtension,
}

impl error::Error for DebugCallbackCreationError {}

impl fmt::Display for DebugCallbackCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            fmt,
            "{}",
            match *self {
                DebugCallbackCreationError::MissingExtension => {
                    "the `EXT_debug_utils` extension was not enabled"
                }
            }
        )
    }
}

impl From<Error> for DebugCallbackCreationError {
    #[inline]
    fn from(err: Error) -> DebugCallbackCreationError {
        panic!("unexpected error: {:?}", err)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    #[test]
    fn ensure_sendable() {
        // It's useful to be able to initialize a DebugCallback on one thread
        // and keep it alive on another thread.
        let instance = instance!();
        let severity = MessageSeverity::none();
        let ty = MessageType::all();
        let callback = DebugCallback::new(&instance, severity, ty, |_| {});
        thread::spawn(move || {
            let _ = callback;
        });
    }
}
<|endoftext|>


<commit_before>//! Completes mod declarations.

use std::iter;

use hir::{Module, ModuleSource};
use ide_db::FxHashSet;
use ide_db::{
    base_db::{SourceDatabaseExt, VfsPath},
    RootDatabase, SymbolKind,
};

use crate::{context::NameContext, CompletionItem};

use crate::{context::CompletionContext, Completions};

/// Complete mod declaration, i.e. `mod $0;`
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let mod_under_caret = match &ctx.name_ctx {
        Some(NameContext::Module(mod_under_caret)) if mod_under_caret.item_list().is_none() => {
            mod_under_caret
        }
        _ => return None,
    };

    let _p = profile::span("completion::complete_mod");

    let current_module = ctx.module;

    let module_definition_file =
        current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
    let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
    let directory_to_look_for_submodules = directory_to_look_for_submodules(
        current_module,
        ctx.db,
        source_root.path_for_file(&module_definition_file)?,
    )?;

    let existing_mod_declarations = current_module
        .children(ctx.db)
        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
        .collect::<FxHashSet<_>>();

    let module_declaration_file =
        current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
            module_declaration_source_file.file_id.original_file(ctx.db)
        });

    source_root
        .iter()
        .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
        .filter(|submodule_candidate_file| {
            Some(submodule_candidate_file) != module_declaration_file.as_ref()
        })
        .filter_map(|submodule_file| {
            let submodule_path = source_root.path_for_file(&submodule_file)?;
            let directory_with_submodule = submodule_path.parent()?;
            let (name, ext) = submodule_path.name_and_extension()?;
            if ext != Some("rs") {
                return None;
            }
            match name {
                "lib" | "main" => None,
                "mod" => {
                    if directory_with_submodule.parent()? == directory_to_look_for_submodules {
                        match directory_with_submodule.name_and_extension()? {
                            (directory_name, None) => Some(directory_name.to_owned()),
                            _ => None,
                        }
                    } else {
                        None
                    }
                }
                file_name if directory_with_submodule == directory_to_look_for_submodules => {
                    Some(file_name.to_owned())
                }
                _ => None,
            }
        })
        .filter(|name| !existing_mod_declarations.contains(name))
        .for_each(|submodule_name| {
            let mut label = submodule_name;
            if mod_under_caret.semicolon_token().is_none() {
                label.push(';');
            }
            let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
            item.add_to(acc)
        });

    Some(())
}

fn directory_to_look_for_submodules(
    module: Module,
    db: &RootDatabase,
    module_file_path: &VfsPath,
) -> Option<VfsPath> {
    let directory_with_module_path = module_file_path.parent()?;
    let (name, ext) = module_file_path.name_and_extension()?;
    if ext != Some("rs") {
        return None;
    }
    let base_directory = match name {
        "mod" | "lib" | "main" => Some(directory_with_module_path),
        regular_rust_file_name => {
            if matches!(
                (
                    directory_with_module_path
                        .parent()
                        .as_ref()
                        .and_then(|path| path.name_and_extension()),
                    directory_with_module_path.name_and_extension(),
                ),
                (Some(("src", None)), Some(("bin", None)))
            ) {
                // files in /src/bin/ can import each other directly
                Some(directory_with_module_path)
            } else {
                directory_with_module_path.join(regular_rust_file_name)
            }
        }
    }?;

    module_chain_to_containing_module_file(module, db)
        .into_iter()
        .filter_map(|module| module.name(db))
        .try_fold(base_directory, |path, name| path.join(&name.to_smol_str()))
}

fn module_chain_to_containing_module_file(
    current_module: Module,
    db: &RootDatabase,
) -> Vec<Module> {
    let mut path =
        iter::successors(Some(current_module), |current_module| current_module.parent(db))
            .take_while(|current_module| {
                matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
            })
            .collect::<Vec<_>>();
    path.reverse();
    path
}

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

    use crate::tests::completion_list;

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn lib_module_completion() {
        check(
            r#"
//- /lib.rs
mod $0
//- /foo.rs
fn foo() {}
//- /foo/ignored_foo.rs
fn ignored_foo() {}
//- /bar/mod.rs
fn bar() {}
//- /bar/ignored_bar.rs
fn ignored_bar() {}
"#,
            expect![[r#"
                md foo;
                md bar;
            "#]],
        );
    }

    #[test]
    fn no_module_completion_with_module_body() {
        check(
            r#"
//- /lib.rs
mod $0 {

}
//- /foo.rs
fn foo() {}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn main_module_completion() {
        check(
            r#"
//- /main.rs
mod $0
//- /foo.rs
fn foo() {}
//- /foo/ignored_foo.rs
fn ignored_foo() {}
//- /bar/mod.rs
fn bar() {}
//- /bar/ignored_bar.rs
fn ignored_bar() {}
"#,
            expect![[r#"
                md foo;
                md bar;
            "#]],
        );
    }

    #[test]
    fn main_test_module_completion() {
        check(
            r#"
//- /main.rs
mod tests {
    mod $0;
}
//- /tests/foo.rs
fn foo() {}
"#,
            expect![[r#"
                md foo
            "#]],
        );
    }

    #[test]
    fn directly_nested_module_completion() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod $0;
//- /foo/bar.rs
fn bar() {}
//- /foo/bar/ignored_bar.rs
fn ignored_bar() {}
//- /foo/baz/mod.rs
fn baz() {}
//- /foo/moar/ignored_moar.rs
fn ignored_moar() {}
"#,
            expect![[r#"
                md bar
                md baz
            "#]],
        );
    }

    #[test]
    fn nested_in_source_module_completion() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod bar {
    mod $0
}
//- /foo/bar/baz.rs
fn baz() {}
"#,
            expect![[r#"
                md baz;
            "#]],
        );
    }

    // FIXME binary modules are not supported in tests properly
    // Binary modules are a bit special, they allow importing the modules from `/src/bin`
    // and that's why are good to test two things:
    // * no cycles are allowed in mod declarations
    // * no modules from the parent directory are proposed
    // Unfortunately, binary modules support is in cargo not rustc,
    // hence the test does not work now
    //
    // #[test]
    // fn regular_bin_module_completion() {
    //     check(
    //         r#"
    //         //- /src/bin.rs
    //         fn main() {}
    //         //- /src/bin/foo.rs
    //         mod $0
    //         //- /src/bin/bar.rs
    //         fn bar() {}
    //         //- /src/bin/bar/bar_ignored.rs
    //         fn bar_ignored() {}
    //     "#,
    //         expect![[r#"
    //             md bar;
    //         "#]],foo
    //     );
    // }

    #[test]
    fn already_declared_bin_module_completion_omitted() {
        check(
            r#"
//- /src/bin.rs crate:main
fn main() {}
//- /src/bin/foo.rs
mod $0
//- /src/bin/bar.rs
mod foo;
fn bar() {}
//- /src/bin/bar/bar_ignored.rs
fn bar_ignored() {}
"#,
            expect![[r#""#]],
        );
    }
}
<commit_msg>Auto merge of #12111 - jonas-schievink:fix-outline-mod-completion, r=jonas-schievink<commit_after>//! Completes mod declarations.

use std::iter;

use hir::{Module, ModuleSource};
use ide_db::FxHashSet;
use ide_db::{
    base_db::{SourceDatabaseExt, VfsPath},
    RootDatabase, SymbolKind,
};
use syntax::{ast, AstNode, SyntaxKind};

use crate::{context::NameContext, CompletionItem};

use crate::{context::CompletionContext, Completions};

/// Complete mod declaration, i.e. `mod $0;`
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let mod_under_caret = match &ctx.name_ctx {
        Some(NameContext::Module(mod_under_caret)) if mod_under_caret.item_list().is_none() => {
            mod_under_caret
        }
        _ => return None,
    };

    let _p = profile::span("completion::complete_mod");

    let mut current_module = ctx.module;
    // For `mod $0`, `ctx.module` is its parent, but for `mod f$0`, it's `mod f` itself, but we're
    // interested in its parent.
    if ctx.original_token.kind() == SyntaxKind::IDENT {
        if let Some(module) = ctx.original_token.ancestors().nth(1).and_then(ast::Module::cast) {
            match current_module.definition_source(ctx.db).value {
                ModuleSource::Module(src) if src == module => {
                    if let Some(parent) = current_module.parent(ctx.db) {
                        current_module = parent;
                    }
                }
                _ => {}
            }
        }
    }

    let module_definition_file =
        current_module.definition_source(ctx.db).file_id.original_file(ctx.db);
    let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file));
    let directory_to_look_for_submodules = directory_to_look_for_submodules(
        current_module,
        ctx.db,
        source_root.path_for_file(&module_definition_file)?,
    )?;

    let existing_mod_declarations = current_module
        .children(ctx.db)
        .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
        .collect::<FxHashSet<_>>();

    let module_declaration_file =
        current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
            module_declaration_source_file.file_id.original_file(ctx.db)
        });

    source_root
        .iter()
        .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file)
        .filter(|submodule_candidate_file| {
            Some(submodule_candidate_file) != module_declaration_file.as_ref()
        })
        .filter_map(|submodule_file| {
            let submodule_path = source_root.path_for_file(&submodule_file)?;
            let directory_with_submodule = submodule_path.parent()?;
            let (name, ext) = submodule_path.name_and_extension()?;
            if ext != Some("rs") {
                return None;
            }
            match name {
                "lib" | "main" => None,
                "mod" => {
                    if directory_with_submodule.parent()? == directory_to_look_for_submodules {
                        match directory_with_submodule.name_and_extension()? {
                            (directory_name, None) => Some(directory_name.to_owned()),
                            _ => None,
                        }
                    } else {
                        None
                    }
                }
                file_name if directory_with_submodule == directory_to_look_for_submodules => {
                    Some(file_name.to_owned())
                }
                _ => None,
            }
        })
        .filter(|name| !existing_mod_declarations.contains(name))
        .for_each(|submodule_name| {
            let mut label = submodule_name;
            if mod_under_caret.semicolon_token().is_none() {
                label.push(';');
            }
            let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
            item.add_to(acc)
        });

    Some(())
}

fn directory_to_look_for_submodules(
    module: Module,
    db: &RootDatabase,
    module_file_path: &VfsPath,
) -> Option<VfsPath> {
    let directory_with_module_path = module_file_path.parent()?;
    let (name, ext) = module_file_path.name_and_extension()?;
    if ext != Some("rs") {
        return None;
    }
    let base_directory = match name {
        "mod" | "lib" | "main" => Some(directory_with_module_path),
        regular_rust_file_name => {
            if matches!(
                (
                    directory_with_module_path
                        .parent()
                        .as_ref()
                        .and_then(|path| path.name_and_extension()),
                    directory_with_module_path.name_and_extension(),
                ),
                (Some(("src", None)), Some(("bin", None)))
            ) {
                // files in /src/bin/ can import each other directly
                Some(directory_with_module_path)
            } else {
                directory_with_module_path.join(regular_rust_file_name)
            }
        }
    }?;

    module_chain_to_containing_module_file(module, db)
        .into_iter()
        .filter_map(|module| module.name(db))
        .try_fold(base_directory, |path, name| path.join(&name.to_smol_str()))
}

fn module_chain_to_containing_module_file(
    current_module: Module,
    db: &RootDatabase,
) -> Vec<Module> {
    let mut path =
        iter::successors(Some(current_module), |current_module| current_module.parent(db))
            .take_while(|current_module| {
                matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
            })
            .collect::<Vec<_>>();
    path.reverse();
    path
}

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

    use crate::tests::completion_list;

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn lib_module_completion() {
        check(
            r#"
//- /lib.rs
mod $0
//- /foo.rs
fn foo() {}
//- /foo/ignored_foo.rs
fn ignored_foo() {}
//- /bar/mod.rs
fn bar() {}
//- /bar/ignored_bar.rs
fn ignored_bar() {}
"#,
            expect![[r#"
                md foo;
                md bar;
            "#]],
        );
    }

    #[test]
    fn no_module_completion_with_module_body() {
        check(
            r#"
//- /lib.rs
mod $0 {

}
//- /foo.rs
fn foo() {}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn main_module_completion() {
        check(
            r#"
//- /main.rs
mod $0
//- /foo.rs
fn foo() {}
//- /foo/ignored_foo.rs
fn ignored_foo() {}
//- /bar/mod.rs
fn bar() {}
//- /bar/ignored_bar.rs
fn ignored_bar() {}
"#,
            expect![[r#"
                md foo;
                md bar;
            "#]],
        );
    }

    #[test]
    fn main_test_module_completion() {
        check(
            r#"
//- /main.rs
mod tests {
    mod $0;
}
//- /tests/foo.rs
fn foo() {}
"#,
            expect![[r#"
                md foo
            "#]],
        );
    }

    #[test]
    fn directly_nested_module_completion() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod $0;
//- /foo/bar.rs
fn bar() {}
//- /foo/bar/ignored_bar.rs
fn ignored_bar() {}
//- /foo/baz/mod.rs
fn baz() {}
//- /foo/moar/ignored_moar.rs
fn ignored_moar() {}
"#,
            expect![[r#"
                md bar
                md baz
            "#]],
        );
    }

    #[test]
    fn nested_in_source_module_completion() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod bar {
    mod $0
}
//- /foo/bar/baz.rs
fn baz() {}
"#,
            expect![[r#"
                md baz;
            "#]],
        );
    }

    // FIXME binary modules are not supported in tests properly
    // Binary modules are a bit special, they allow importing the modules from `/src/bin`
    // and that's why are good to test two things:
    // * no cycles are allowed in mod declarations
    // * no modules from the parent directory are proposed
    // Unfortunately, binary modules support is in cargo not rustc,
    // hence the test does not work now
    //
    // #[test]
    // fn regular_bin_module_completion() {
    //     check(
    //         r#"
    //         //- /src/bin.rs
    //         fn main() {}
    //         //- /src/bin/foo.rs
    //         mod $0
    //         //- /src/bin/bar.rs
    //         fn bar() {}
    //         //- /src/bin/bar/bar_ignored.rs
    //         fn bar_ignored() {}
    //     "#,
    //         expect![[r#"
    //             md bar;
    //         "#]],foo
    //     );
    // }

    #[test]
    fn already_declared_bin_module_completion_omitted() {
        check(
            r#"
//- /src/bin.rs crate:main
fn main() {}
//- /src/bin/foo.rs
mod $0
//- /src/bin/bar.rs
mod foo;
fn bar() {}
//- /src/bin/bar/bar_ignored.rs
fn bar_ignored() {}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn name_partially_typed() {
        check(
            r#"
//- /lib.rs
mod f$0
//- /foo.rs
fn foo() {}
//- /foo/ignored_foo.rs
fn ignored_foo() {}
//- /bar/mod.rs
fn bar() {}
//- /bar/ignored_bar.rs
fn ignored_bar() {}
"#,
            expect![[r#"
                md foo;
                md bar;
            "#]],
        );
    }
}
<|endoftext|>


<commit_before>use geojson::Geometry;
use serde::Serialize;
use tracing::{debug, instrument};
use warp::http::StatusCode;
use warp::reply::{json, with_status};

use crate::adapters::primary::bragi::api::ForwardGeocoderExplainQuery;
use crate::adapters::primary::{
    bragi::api::{
        BragiStatus, ElasticsearchStatus, ForwardGeocoderQuery, MimirStatus, ReverseGeocoderQuery,
        StatusResponseBody, Type,
    },
    common::{
        dsl, filters, geocoding::Feature, geocoding::FromWithLang, geocoding::GeocodeJsonResponse,
        settings,
    },
};
use crate::domain::model::configuration::{root_doctype, root_doctype_dataset};
use crate::domain::model::query::Query;
use crate::domain::ports::primary::explain_query::ExplainDocument;
use crate::domain::ports::primary::search_documents::SearchDocuments;
use crate::domain::ports::primary::status::Status;
use common::document::ContainerDocument;
use places::{addr::Addr, admin::Admin, poi::Poi, stop::Stop, street::Street, Place};

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[instrument(skip(client, settings))]
pub async fn forward_geocoder<S>(
    params: ForwardGeocoderQuery,
    geometry: Option<Geometry>,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: SearchDocuments,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let q = params.q.clone();
    let timeout = params.timeout;
    let es_indices_to_search_in = build_es_indices_to_search(&params);
    let filters = filters::Filters::from((params, geometry));
    let dsl = dsl::build_query(&q, filters.clone(), &["fr"], &settings);

    debug!("{}", serde_json::to_string(&dsl).unwrap());

    match client
        .search_documents(
            es_indices_to_search_in,
            Query::QueryDSL(dsl),
            filters.limit,
            timeout,
        )
        .await
    {
        Ok(res) => {
            let places: Result<Vec<Place>, serde_json::Error> = res
                .into_iter()
                .map(|json| serde_json::from_value::<Place>(json.into()))
                .collect();

            match places {
                Ok(places) => {
                    let features = places
                        .into_iter()
                        .map(|p| Feature::from_with_lang(p, None)) // FIXME lang: None
                        .collect();
                    let resp = GeocodeJsonResponse::new(q, features);
                    Ok(with_status(json(&resp), StatusCode::OK))
                }
                Err(err) => Ok(with_status(
                    json(&format!(
                        "Error while searching {}: {}",
                        &q,
                        err.to_string()
                    )),
                    StatusCode::INTERNAL_SERVER_ERROR,
                )),
            }
        }
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while searching {}: {}",
                &q,
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

#[instrument(skip(client, settings))]
pub async fn forward_geocoder_explain<S>(
    params: ForwardGeocoderExplainQuery,
    geometry: Option<Geometry>,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: ExplainDocument,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let q = params.query.q.clone();
    let filters = filters::Filters::from((params.query, geometry));
    let dsl = dsl::build_query(&q, filters, &["fr"], &settings);

    debug!("{}", serde_json::to_string(&dsl).unwrap());

    match client
        .explain_document(Query::QueryDSL(dsl), params.doc_id, params.doc_type)
        .await
    {
        Ok(res) => Ok(with_status(json(&res), StatusCode::OK)),
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while searching {}: {}",
                &q,
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

pub async fn reverse_geocoder<S>(
    params: ReverseGeocoderQuery,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: SearchDocuments,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let distance = format!("{}m", settings.reverse_query.radius);
    let dsl = dsl::build_reverse_query(&distance, params.lat, params.lon);

    let es_indices_to_search_in = vec![
        root_doctype(Street::static_doc_type()),
        root_doctype(Addr::static_doc_type()),
    ];

    match client
        .search_documents(
            es_indices_to_search_in,
            Query::QueryDSL(dsl),
            params.limit,
            params.timeout,
        )
        .await
    {
        Ok(res) => {
            let places = res
                .into_iter()
                .map(|json| serde_json::from_value::<Place>(json.into()).unwrap())
                .collect();

            let resp = GeocodeJsonResponse::from_with_lang(places, None);
            Ok(with_status(json(&resp), StatusCode::OK))
        }
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while reverse searching: {}",
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

pub async fn status<S>(client: S, url: String) -> Result<impl warp::Reply, warp::Rejection>
where
    S: Status,
{
    match client.status().await {
        Ok(res) => {
            let resp = StatusResponseBody {
                bragi: BragiStatus {
                    version: VERSION.to_string(),
                },
                mimir: MimirStatus {
                    version: res.version,
                },
                elasticsearch: ElasticsearchStatus {
                    version: res.storage.version,
                    health: res.storage.health.to_string(),
                    url,
                },
            };
            Ok(with_status(json(&resp), StatusCode::OK))
        }
        Err(err) => Ok(with_status(
            json(&format!("Error while querying status: {}", err.to_string())),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

fn build_es_indices_to_search(query: &ForwardGeocoderQuery) -> Vec<String> {
    // some specific types are requested,
    // let's search only for these types of objects
    if let Some(types) = &query.types {
        let mut indices = Vec::new();
        for doc_type in types.iter() {
            match doc_type {
                Type::House => indices.push(root_doctype(Addr::static_doc_type())),
                Type::Street => indices.push(root_doctype(Street::static_doc_type())),
                Type::Zone => indices.push(root_doctype(Admin::static_doc_type())),
                Type::Poi => {
                    let doc_type_str = Poi::static_doc_type();
                    // if some poi_dataset are specified
                    // we search for poi only in the corresponding es indices
                    if let Some(poi_datasets) = &query.poi_dataset {
                        for poi_dataset in poi_datasets.iter() {
                            indices.push(root_doctype_dataset(doc_type_str, poi_dataset));
                        }
                    } else {
                        // no poi_dataset specified
                        // we search in the global alias for all poi
                        indices.push(root_doctype(doc_type_str));
                    }
                }
                Type::StopArea => {
                    // if some pt_dataset are specified
                    // we search for stops only in the corresponding es indices
                    let doc_type_str = Stop::static_doc_type();
                    if let Some(pt_datasets) = &query.pt_dataset {
                        for pt_dataset in pt_datasets.iter() {
                            indices.push(root_doctype_dataset(doc_type_str, pt_dataset));
                        }
                    } else {
                        // no pt_dataset specified
                        // we search in the global alias for all stops
                        indices.push(root_doctype(doc_type_str));
                    }
                }
            }
        }
        indices
    }
    // no types specified, we search for all objects in all indices
    else {
        vec![
            root_doctype(Admin::static_doc_type()),
            root_doctype(Street::static_doc_type()),
            root_doctype(Addr::static_doc_type()),
            root_doctype(Stop::static_doc_type()),
            root_doctype(Poi::static_doc_type()),
        ]
    }
}
<commit_msg>add tests WIP<commit_after>use geojson::Geometry;
use serde::Serialize;
use tracing::{debug, instrument};
use warp::http::StatusCode;
use warp::reply::{json, with_status};

use crate::adapters::primary::bragi::api::ForwardGeocoderExplainQuery;
use crate::adapters::primary::{
    bragi::api::{
        BragiStatus, ElasticsearchStatus, ForwardGeocoderQuery, MimirStatus, ReverseGeocoderQuery,
        StatusResponseBody, Type,
    },
    common::{
        dsl, filters, geocoding::Feature, geocoding::FromWithLang, geocoding::GeocodeJsonResponse,
        settings,
    },
};
use crate::domain::model::configuration::{root_doctype, root_doctype_dataset};
use crate::domain::model::query::Query;
use crate::domain::ports::primary::explain_query::ExplainDocument;
use crate::domain::ports::primary::search_documents::SearchDocuments;
use crate::domain::ports::primary::status::Status;
use common::document::ContainerDocument;
use places::{addr::Addr, admin::Admin, poi::Poi, stop::Stop, street::Street, Place};

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[instrument(skip(client, settings))]
pub async fn forward_geocoder<S>(
    params: ForwardGeocoderQuery,
    geometry: Option<Geometry>,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: SearchDocuments,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let q = params.q.clone();
    let timeout = params.timeout;
    let es_indices_to_search_in = build_es_indices_to_search(&params);
    let filters = filters::Filters::from((params, geometry));
    let dsl = dsl::build_query(&q, filters.clone(), &["fr"], &settings);

    debug!("{}", serde_json::to_string(&dsl).unwrap());

    match client
        .search_documents(
            es_indices_to_search_in,
            Query::QueryDSL(dsl),
            filters.limit,
            timeout,
        )
        .await
    {
        Ok(res) => {
            let places: Result<Vec<Place>, serde_json::Error> = res
                .into_iter()
                .map(|json| serde_json::from_value::<Place>(json.into()))
                .collect();

            match places {
                Ok(places) => {
                    let features = places
                        .into_iter()
                        .map(|p| Feature::from_with_lang(p, None)) // FIXME lang: None
                        .collect();
                    let resp = GeocodeJsonResponse::new(q, features);
                    Ok(with_status(json(&resp), StatusCode::OK))
                }
                Err(err) => Ok(with_status(
                    json(&format!(
                        "Error while searching {}: {}",
                        &q,
                        err.to_string()
                    )),
                    StatusCode::INTERNAL_SERVER_ERROR,
                )),
            }
        }
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while searching {}: {}",
                &q,
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

#[instrument(skip(client, settings))]
pub async fn forward_geocoder_explain<S>(
    params: ForwardGeocoderExplainQuery,
    geometry: Option<Geometry>,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: ExplainDocument,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let q = params.query.q.clone();
    let filters = filters::Filters::from((params.query, geometry));
    let dsl = dsl::build_query(&q, filters, &["fr"], &settings);

    debug!("{}", serde_json::to_string(&dsl).unwrap());

    match client
        .explain_document(Query::QueryDSL(dsl), params.doc_id, params.doc_type)
        .await
    {
        Ok(res) => Ok(with_status(json(&res), StatusCode::OK)),
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while searching {}: {}",
                &q,
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

pub async fn reverse_geocoder<S>(
    params: ReverseGeocoderQuery,
    client: S,
    settings: settings::QuerySettings,
) -> Result<impl warp::Reply, warp::Rejection>
where
    S: SearchDocuments,
    S::Document: Serialize + Into<serde_json::Value>,
{
    let distance = format!("{}m", settings.reverse_query.radius);
    let dsl = dsl::build_reverse_query(&distance, params.lat, params.lon);

    let es_indices_to_search_in = vec![
        root_doctype(Street::static_doc_type()),
        root_doctype(Addr::static_doc_type()),
    ];

    match client
        .search_documents(
            es_indices_to_search_in,
            Query::QueryDSL(dsl),
            params.limit,
            params.timeout,
        )
        .await
    {
        Ok(res) => {
            let places = res
                .into_iter()
                .map(|json| serde_json::from_value::<Place>(json.into()).unwrap())
                .collect();

            let resp = GeocodeJsonResponse::from_with_lang(places, None);
            Ok(with_status(json(&resp), StatusCode::OK))
        }
        Err(err) => Ok(with_status(
            json(&format!(
                "Error while reverse searching: {}",
                err.to_string()
            )),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

pub async fn status<S>(client: S, url: String) -> Result<impl warp::Reply, warp::Rejection>
where
    S: Status,
{
    match client.status().await {
        Ok(res) => {
            let resp = StatusResponseBody {
                bragi: BragiStatus {
                    version: VERSION.to_string(),
                },
                mimir: MimirStatus {
                    version: res.version,
                },
                elasticsearch: ElasticsearchStatus {
                    version: res.storage.version,
                    health: res.storage.health.to_string(),
                    url,
                },
            };
            Ok(with_status(json(&resp), StatusCode::OK))
        }
        Err(err) => Ok(with_status(
            json(&format!("Error while querying status: {}", err.to_string())),
            StatusCode::INTERNAL_SERVER_ERROR,
        )),
    }
}

fn build_es_indices_to_search(query: &ForwardGeocoderQuery) -> Vec<String> {
    // some specific types are requested,
    // let's search only for these types of objects
    if let Some(types) = &query.types {
        let mut indices = Vec::new();
        for doc_type in types.iter() {
            match doc_type {
                Type::House => indices.push(root_doctype(Addr::static_doc_type())),
                Type::Street => indices.push(root_doctype(Street::static_doc_type())),
                Type::Zone => indices.push(root_doctype(Admin::static_doc_type())),
                Type::Poi => {
                    let doc_type_str = Poi::static_doc_type();
                    // if some poi_dataset are specified
                    // we search for poi only in the corresponding es indices
                    if let Some(poi_datasets) = &query.poi_dataset {
                        for poi_dataset in poi_datasets.iter() {
                            indices.push(root_doctype_dataset(doc_type_str, poi_dataset));
                        }
                    } else {
                        // no poi_dataset specified
                        // we search in the global alias for all poi
                        indices.push(root_doctype(doc_type_str));
                    }
                }
                Type::StopArea => {
                    // if some pt_dataset are specified
                    // we search for stops only in the corresponding es indices
                    let doc_type_str = Stop::static_doc_type();
                    if let Some(pt_datasets) = &query.pt_dataset {
                        for pt_dataset in pt_datasets.iter() {
                            indices.push(root_doctype_dataset(doc_type_str, pt_dataset));
                        }
                    } else {
                        // no pt_dataset specified
                        // we search in the global alias for all stops
                        indices.push(root_doctype(doc_type_str));
                    }
                }
            }
        }
        indices
    }
    // no types specified, we search for all objects in all indices
    else {
        vec![
            root_doctype(Admin::static_doc_type()),
            root_doctype(Street::static_doc_type()),
            root_doctype(Addr::static_doc_type()),
            root_doctype(Stop::static_doc_type()),
            root_doctype(Poi::static_doc_type()),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::adapters::primary::bragi::routes::forward_geocoder_get;

    async fn indices_builder(query: &str) -> Vec<String> {
        let filter = forward_geocoder_get();
        let params = warp::test::request()
            .path(query)
            .filter(&filter)
            .await
            .unwrap();
        println!("{:?}", params.0);
        build_es_indices_to_search(&params.0)
    }

    // no dataset and no types
    #[tokio::test]
    async fn no_dataset_no_type() {
        let es_indices = indices_builder("/api/v1/autocomplete?q=Bob").await;
        assert_eq!(
            es_indices,
            [
                "munin_admin",
                "munin_street",
                "munin_addr",
                "munin_stop",
                "munin_poi"
            ]
        );
    }

    // no dataset + type public_transport:stop_area only
    #[tokio::test]
    async fn no_dataset_with_type_sa() {
        let es_indices =
            indices_builder("/api/v1/autocomplete?q=Bob&type[]=public_transport:stop_area").await;
        assert_eq!(es_indices, ["munin_stop",]);
    }

    // no dataset + types poi, city, street, house
    #[tokio::test]
    async fn no_dataset_all_types_but_sa() {
        let es_indices = indices_builder(
            "/api/v1/autocomplete?q=Bob&type[]=poi&type[]=city&type[]=street&type[]=house",
        )
        .await;
        assert_eq!(
            es_indices,
            [
                "munin_admin",
                "munin_street",
                "munin_addr",
                "munin_stop",
                "munin_poi"
            ]
        );
    }

    // no dataset + types poi, city, street, house and public_transport:stop_area
    #[tokio::test]
    async fn no_dataset_all_types() {
        let es_indices = indices_builder(
            "/api/v1/autocomplete?q=Bob&type[]=poi&type[]=city&type[]=street&type[]=house&type[]=public_transport:stop_area",
        )
        .await;
        assert_eq!(
            es_indices,
            [
                "munin_admin",
                "munin_street",
                "munin_addr",
                "munin_stop",
                "munin_poi"
            ]
        );
    }

    // dataset fr + no type
    #[tokio::test]
    async fn fr_dataset_no_type() {
        let es_indices = indices_builder("/api/v1/autocomplete?q=Bob&pt_dataset[]=fr").await;
        assert_eq!(es_indices, ["munin_admin",]);
    }

    // dataset fr + type public_transport:stop_area only
    #[tokio::test]
    async fn fr_pt_dataset_with_type_sa() {
        let es_indices = indices_builder(
            "/api/v1/autocomplete?q=Bob&pt_dataset[]=fr&type[]=public_transport:stop_area",
        )
        .await;
        assert_eq!(es_indices, ["munin_stop_dataset1"]);
    }

    // no dataset + types poi, city, street, house
    #[tokio::test]
    async fn fr_dataset_all_types_but_sa() {
        let es_indices = indices_builder(
            "/api/v1/autocomplete?q=Bob&pt_dataset[]=fr&type[]=poi&type[]=city&type[]=street&type[]=house",
        )
            .await;
        assert_eq!(
            es_indices,
            [
                "munin_admin",
                "munin_street",
                "munin_addr",
                "munin_stop",
                "munin_poi"
            ]
        );
    }

    // dataset fr + types poi, city, street, house and public_transport:stop_area
    #[tokio::test]
    async fn fr_dataset_all_types() {
        let es_indices = indices_builder("/api/v1/autocomplete?q=Bob&pt_dataset[]=fr&type[]=poi&type[]=city&type[]=street&type[]=house&type[]=public_transport:stop_area").await;
        assert_eq!(es_indices, ["munin_admin",]);
    }

    // dataset fr + poi_dataset mti + types poi, city, street, house
    #[tokio::test]
    async fn fr_dataset_mti_poi_dataset_all_types_but_sa() {
        let es_indices = indices_builder("/api/v1/autocomplete?q=Bob&pt_dataset[]=fr&poi_dataset[]=mti&type[]=poi&type[]=city&type[]=street&type[]=house").await;
        assert_eq!(es_indices, ["munin_admin",]);
    }
}
<|endoftext|>


<commit_before>//! Traits for types which can be converted from node attributes.

use std::io::Result as IoResult;

use parser::binary::{Result, ParserSource, Attributes, Attribute};
use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};

/// Types which can be converted from a node attribute.
pub trait AttributeValue: Sized {
    /// Reads the given attribute as `Self` type.
    ///
    /// The value type will be strictly checked.
    ///
    /// Returns `Ok(Some(Self))` if successfully converted,
    /// `Ok(None)` if successfully read but the types didn't matched,
    /// `Err(_)` if parse error happened.
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource;

    /// Reads the given attribute and converts into `Self` type.
    ///
    /// The value type will be loosely checked.
    ///
    /// Returns `Ok(Some(Self))` if successfully converted,
    /// `Ok(None)` if successfully read but the types were incompatible,
    /// `Err(_)` if parse error happened.
    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource;
}

// Simply ignore the attribute.
impl AttributeValue for () {
    fn from_attribute<R>(_: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Ok(Some(()))
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for bool {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::Bool(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for i16 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I16(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for i32 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I32(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Primitive(PrimitiveAttribute::I16(val)) => Ok(Some(val as i32)),
            Attribute::Primitive(PrimitiveAttribute::I32(val)) => Ok(Some(val)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for i64 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I64(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Primitive(PrimitiveAttribute::I16(val)) => Ok(Some(val as i64)),
            Attribute::Primitive(PrimitiveAttribute::I32(val)) => Ok(Some(val as i64)),
            Attribute::Primitive(PrimitiveAttribute::I64(val)) => Ok(Some(val)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for f32 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::F32(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(attr) = attr {
            Ok(attr.as_f32())
        } else {
            Ok(None)
        }
    }
}

impl AttributeValue for f64 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::F64(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(attr) = attr {
            Ok(attr.as_f64())
        } else {
            Ok(None)
        }
    }
}

impl AttributeValue for Vec<i32> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::I32(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for Vec<i64> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::I64(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(Into::into))
                    .collect::<IoResult<_>>()?))
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => Ok(Some(arr.into_vec()?)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for Vec<f32> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F32(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => Ok(Some(arr.into_vec()?)),
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(|v| v as f32))
                    .collect::<IoResult<_>>()?))
            },
            _ => Ok(None),
        }
    }
}

impl AttributeValue for Vec<f64> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F64(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(Into::into))
                    .collect::<IoResult<_>>()?))
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => Ok(Some(arr.into_vec()?)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for String {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            if val.value_type() == SpecialAttributeType::String {
                return Ok(Some(val.into_string()?));
            }
        }
        Ok(None)
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for Vec<u8> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            if val.value_type() == SpecialAttributeType::Binary {
                return Ok(Some(val.into_vec()?));
            }
        }
        Ok(None)
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            Ok(Some(val.into_vec()?))
        } else {
            Ok(None)
        }
    }
}

/// Attribute values list (tuple).
pub trait AttributeValues: Sized {
    /// Construct `Self` via a conversion if possible.
    ///
    /// Returns `Ok(Some(values))` if successfully converted,
    /// `Ok(None)` if the parsing is successfully done but types are incompatible,
    /// `Err(_)` if the parsing failed.
    fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
    where
        R: ParserSource;
}

macro_rules! impl_attribute_values {
    ($($name:ident: $t:ident),+,) => {
        impl<$($t: AttributeValue),+> AttributeValues for ($($t),+,) {
            fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
                where R: ParserSource
            {
                $(
                    let $name = {
                        let attr = match attrs.next_attribute()? {
                            Some(attr) => attr,
                            None => return Ok(None),
                        };
                        match $t::from_attribute(attr)? {
                            Some(val) => val,
                            None => return Ok(None),
                        }
                    };
                )+
                Ok(Some(($($name),+,)))
            }
        }
    }
}

impl<T: AttributeValue> AttributeValues for T {
    fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        <(T,)>::from_attributes(attrs).map(|v_opt| v_opt.map(|v| v.0))
    }
}

impl_attribute_values! {
    t1: T1,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
    t7: T7,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
    t7: T7,
    t8: T8,
}
<commit_msg>Implement `AttributeValue` for `Vec<[f32; 3]>` and `Vec<[f64; 3]>`<commit_after>//! Traits for types which can be converted from node attributes.

use std::io::Result as IoResult;

use parser::binary::{Result, ParserSource, Attributes, Attribute, ArrayAttributeReader};
use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};

/// Types which can be converted from a node attribute.
pub trait AttributeValue: Sized {
    /// Reads the given attribute as `Self` type.
    ///
    /// The value type will be strictly checked.
    ///
    /// Returns `Ok(Some(Self))` if successfully converted,
    /// `Ok(None)` if successfully read but the types didn't matched,
    /// `Err(_)` if parse error happened.
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource;

    /// Reads the given attribute and converts into `Self` type.
    ///
    /// The value type will be loosely checked.
    ///
    /// Returns `Ok(Some(Self))` if successfully converted,
    /// `Ok(None)` if successfully read but the types were incompatible,
    /// `Err(_)` if parse error happened.
    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource;
}

// Simply ignore the attribute.
impl AttributeValue for () {
    fn from_attribute<R>(_: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Ok(Some(()))
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for bool {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::Bool(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for i16 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I16(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for i32 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I32(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Primitive(PrimitiveAttribute::I16(val)) => Ok(Some(val as i32)),
            Attribute::Primitive(PrimitiveAttribute::I32(val)) => Ok(Some(val)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for i64 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::I64(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Primitive(PrimitiveAttribute::I16(val)) => Ok(Some(val as i64)),
            Attribute::Primitive(PrimitiveAttribute::I32(val)) => Ok(Some(val as i64)),
            Attribute::Primitive(PrimitiveAttribute::I64(val)) => Ok(Some(val)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for f32 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::F32(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(attr) = attr {
            Ok(attr.as_f32())
        } else {
            Ok(None)
        }
    }
}

impl AttributeValue for f64 {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(PrimitiveAttribute::F64(val)) = attr {
            Ok(Some(val))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Primitive(attr) = attr {
            Ok(attr.as_f64())
        } else {
            Ok(None)
        }
    }
}

impl AttributeValue for Vec<i32> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::I32(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for Vec<i64> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::I64(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(Into::into))
                    .collect::<IoResult<_>>()?))
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => Ok(Some(arr.into_vec()?)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for Vec<f32> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F32(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => Ok(Some(arr.into_vec()?)),
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(|v| v as f32))
                    .collect::<IoResult<_>>()?))
            },
            _ => Ok(None),
        }
    }
}

impl AttributeValue for Vec<f64> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F64(arr)) = attr {
            Ok(Some(arr.into_vec()?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                Ok(Some(arr.into_iter()
                    .map(|v| v.map(Into::into))
                    .collect::<IoResult<_>>()?))
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => Ok(Some(arr.into_vec()?)),
            _ => Ok(None),
        }
    }
}

/// Gets `[f32; 3]` from `ArrayAttributeReader<R, f32>`.
fn array_attr_f32_into_vec3<R>(mut arr: ArrayAttributeReader<R, f32>) -> IoResult<Vec<[f32; 3]>>
where
    R: ParserSource,
{
    let components_len = arr.rest_elements();
    let (num_vecs, remainder) = (components_len / 3, components_len % 3);
    if remainder != 0 {
        warn!(
            "Converting `ArrayAttribute::F32` into `Vec<[f32; 3]>` but array length ({}) is not a multiple of 3",
            components_len
        );
    }
    let mut vec = Vec::with_capacity(num_vecs as usize);
    assert_eq!(
        ::std::mem::align_of::<[f32; 3]>(),
        ::std::mem::align_of::<f32>(),
        "`[[f32; 3]]` should not have padding but it has"
    );
    let components_buf_len = (components_len - remainder) as usize; // Equals to `num_vecs * 3`.
    let components_buf = unsafe {
        ::std::slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut f32, components_buf_len)
    };
    assert!(components_buf.len() <= vec.len() * 3);
    let size = arr.read_into_buf(components_buf)?;
    assert_eq!(size, components_buf_len);
    Ok(vec)
}

/// Gets `[f32; 3]` from `ArrayAttributeReader<R, f32>`.
fn array_attr_f64_into_vec3<R>(mut arr: ArrayAttributeReader<R, f64>) -> IoResult<Vec<[f64; 3]>>
where
    R: ParserSource,
{
    let components_len = arr.rest_elements();
    let (num_vecs, remainder) = (components_len / 3, components_len % 3);
    if remainder != 0 {
        warn!(
            "Converting `ArrayAttribute::F64` into `Vec<[f64; 3]>` but array length ({}) is not a multiple of 3",
            components_len
        );
    }
    let mut vec = Vec::with_capacity(num_vecs as usize);
    assert_eq!(
        ::std::mem::align_of::<[f64; 3]>(),
        ::std::mem::align_of::<f64>(),
        "`[[f64; 3]]` should not have padding but it has"
    );
    let components_buf_len = (components_len - remainder) as usize; // Equals to `num_vecs * 3`.
    let components_buf = unsafe {
        ::std::slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut f64, components_buf_len)
    };
    assert!(components_buf.len() <= vec.len() * 3);
    let size = arr.read_into_buf(components_buf)?;
    assert_eq!(size, components_buf_len);
    Ok(vec)
}

impl AttributeValue for Vec<[f32; 3]> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F32(arr)) = attr {
            Ok(Some(array_attr_f32_into_vec3(arr)?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => Ok(Some(array_attr_f32_into_vec3(arr)?)),
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                let components_len = arr.rest_elements();
                let mut buf = Vec::with_capacity((components_len / 3) as usize);
                // Can't use `Into::into()` because `From<f64>` is not implemented for `f32`.
                let mut iter = arr.into_iter().map(|v| v.map(|f| f as f32));
                while let Some(v0r) = iter.next() {
                    let v0 = v0r?;
                    let v1 = match iter.next() {
                        Some(v) => v?,
                        None => {
                            warn!(
                                "Converting `ArrayAttribute::F64` into `Vec<[f32; 3]>` but array length ({}) is not a multiple of 3",
                                components_len
                            );
                            break;
                        },
                    };
                    let v2 = match iter.next() {
                        Some(v) => v?,
                        None => {
                            warn!(
                                "Converting `ArrayAttribute::F64` into `Vec<[f32; 3]>` but array length ({}) is not a multiple of 3",
                                components_len
                            );
                            break;
                        },
                    };
                    buf.push([v0, v1, v2]);
                }
                Ok(Some(buf))
            },
            _ => Ok(None),
        }
    }
}

impl AttributeValue for Vec<[f64; 3]> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Array(ArrayAttribute::F64(arr)) = attr {
            Ok(Some(array_attr_f64_into_vec3(arr)?))
        } else {
            Ok(None)
        }
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        match attr {
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                let components_len = arr.rest_elements();
                let mut buf = Vec::with_capacity((components_len / 3) as usize);
                let mut iter = arr.into_iter().map(|v| v.map(Into::into));
                while let Some(v0r) = iter.next() {
                    let v0 = v0r?;
                    let v1 = match iter.next() {
                        Some(v) => v?,
                        None => {
                            warn!(
                                "Converting `ArrayAttribute::F32` into `Vec<[f64; 3]>` but array length ({}) is not a multiple of 3",
                                components_len
                            );
                            break;
                        },
                    };
                    let v2 = match iter.next() {
                        Some(v) => v?,
                        None => {
                            warn!(
                                "Converting `ArrayAttribute::F32` into `Vec<[f64; 3]>` but array length ({}) is not a multiple of 3",
                                components_len
                            );
                            break;
                        },
                    };
                    buf.push([v0, v1, v2]);
                }
                Ok(Some(buf))
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => Ok(Some(array_attr_f64_into_vec3(arr)?)),
            _ => Ok(None),
        }
    }
}

impl AttributeValue for String {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            if val.value_type() == SpecialAttributeType::String {
                return Ok(Some(val.into_string()?));
            }
        }
        Ok(None)
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        Self::from_attribute(attr)
    }
}

impl AttributeValue for Vec<u8> {
    fn from_attribute<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            if val.value_type() == SpecialAttributeType::Binary {
                return Ok(Some(val.into_vec()?));
            }
        }
        Ok(None)
    }

    fn from_attribute_loose<R>(attr: Attribute<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        if let Attribute::Special(val) = attr {
            Ok(Some(val.into_vec()?))
        } else {
            Ok(None)
        }
    }
}

/// Attribute values list (tuple).
pub trait AttributeValues: Sized {
    /// Construct `Self` via a conversion if possible.
    ///
    /// Returns `Ok(Some(values))` if successfully converted,
    /// `Ok(None)` if the parsing is successfully done but types are incompatible,
    /// `Err(_)` if the parsing failed.
    fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
    where
        R: ParserSource;
}

macro_rules! impl_attribute_values {
    ($($name:ident: $t:ident),+,) => {
        impl<$($t: AttributeValue),+> AttributeValues for ($($t),+,) {
            fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
                where R: ParserSource
            {
                $(
                    let $name = {
                        let attr = match attrs.next_attribute()? {
                            Some(attr) => attr,
                            None => return Ok(None),
                        };
                        match $t::from_attribute(attr)? {
                            Some(val) => val,
                            None => return Ok(None),
                        }
                    };
                )+
                Ok(Some(($($name),+,)))
            }
        }
    }
}

impl<T: AttributeValue> AttributeValues for T {
    fn from_attributes<R>(attrs: &mut Attributes<R>) -> Result<Option<Self>>
    where
        R: ParserSource,
    {
        <(T,)>::from_attributes(attrs).map(|v_opt| v_opt.map(|v| v.0))
    }
}

impl_attribute_values! {
    t1: T1,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
    t7: T7,
}
impl_attribute_values! {
    t1: T1,
    t2: T2,
    t3: T3,
    t4: T4,
    t5: T5,
    t6: T6,
    t7: T7,
    t8: T8,
}
<|endoftext|>


<commit_before>// Copyright 2013-2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib_ffi;
use glib::IsA;
use glib::object::Downcast;
use glib::translate::*;

use libc::c_char;
use std::boxed::Box as Box_;
use std::mem::transmute;

use EntryCompletion;
use TreeIter;

pub trait EntryCompletionExtManual {
    fn set_match_func<F: Fn(&Self, &str, &TreeIter) -> bool + 'static>(&self, f: F);
}

impl<O: IsA<EntryCompletion>> EntryCompletionExtManual for O {
    fn set_match_func<F: Fn(&Self, &str, &TreeIter) -> bool + 'static>(&self, f: F) {
        unsafe {
            let f: Box_<Box_<Fn(&Self, &str, &TreeIter) -> bool + 'static>> = Box_::new(Box_::new(f));
            let callback = transmute(set_match_func_trampoline::<Self> as usize);
            ffi::gtk_entry_completion_set_match_func(self.to_glib_none().0,
                                                     callback,
                                                     Box_::into_raw(f) as *mut _,
                                                     None);
        }
    }
}

unsafe extern "C" fn set_match_func_trampoline<P>(this: *mut ffi::GtkEntryCompletion,
                                                  key: *const c_char,
                                                  iter: *mut ffi::GtkTreeIter,
                                                  f: glib_ffi::gpointer)
                                                  -> glib_ffi::gboolean
where P: IsA<EntryCompletion> {
    let f: &&(Fn(&P, &str, &TreeIter) -> bool + 'static) = transmute(f);
    f(&EntryCompletion::from_glib_borrow(this).downcast_unchecked(),
      &String::from_glib_none(key),
      &TreeIter::from_glib_borrow(iter)).to_glib()
}
<commit_msg>Add destroy callback<commit_after>// Copyright 2013-2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib_ffi;
use glib::IsA;
use glib::object::Downcast;
use glib::translate::*;

use libc::c_char;
use std::boxed::Box as Box_;
use std::mem::transmute;
use std::cell::RefCell;

use EntryCompletion;
use TreeIter;

pub trait EntryCompletionExtManual {
    fn set_match_func<F: FnMut(&Self, &str, &TreeIter) -> bool>(&self, f: F);
}

impl<O: IsA<EntryCompletion>> EntryCompletionExtManual for O {
    fn set_match_func<F: FnMut(&Self, &str, &TreeIter) -> bool>(&self, f: F) {
        unsafe {
            let f: Box_<RefCell<Box_<FnMut(&Self, &str, &TreeIter) -> bool>>> =
                Box_::new(RefCell::new(Box_::new(f)));
            let callback = transmute(set_match_func_trampoline::<Self> as usize);
            let destroy_callback = transmute(set_match_func_destroy::<Self> as usize);
            ffi::gtk_entry_completion_set_match_func(self.to_glib_none().0,
                                                     callback,
                                                     Box_::into_raw(f) as *mut _,
                                                     destroy_callback);
        }
    }
}

unsafe extern "C" fn set_match_func_trampoline<P>(this: *mut ffi::GtkEntryCompletion,
                                                  key: *const c_char,
                                                  iter: *mut ffi::GtkTreeIter,
                                                  f: glib_ffi::gpointer)
                                                  -> glib_ffi::gboolean
where P: IsA<EntryCompletion> {
    let func: &RefCell<Box<FnMut(&P, &str, &TreeIter) -> bool>> = transmute(f);

    (&mut *func.borrow_mut())(
        &EntryCompletion::from_glib_borrow(this).downcast_unchecked(),
        &String::from_glib_none(key),
        &TreeIter::from_glib_borrow(iter)
    ).to_glib()
}

unsafe extern "C" fn set_match_func_destroy<P>(f: glib_ffi::gpointer)
where P: IsA<EntryCompletion> {
    Box::<RefCell<Box<FnMut(&P, &str, &TreeIter) -> bool>>>::from_raw(
        f as *mut _
    );
}
<|endoftext|>