<commit_before>//! DICOM association module
//!
//! This module contains utilities for establishing associations
//! between DICOM nodes via TCP/IP.

use std::net::TcpStream;

use snafu::{ResultExt, Snafu};

use crate::pdu::{reader::read_pdu, writer::write_pdu, Pdu};

pub mod scp;
pub mod scu;

#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum Error {
    /// failed to send PDU message
    Send { source: crate::pdu::writer::Error },

    /// failed to receive PDU message
    Receive { source: crate::pdu::reader::Error },

    #[snafu(display("unexpected response `{:?}`", pdu))]
    #[non_exhaustive]
    UnexpectedResponse {
        /// the PDU obtained from the other node
        pdu: Pdu,
    },

    #[snafu(display("unknown response  `{:?}`", pdu))]
    #[non_exhaustive]
    UnknownResponse {
        /// the PDU obtained from the other node, of variant Unknown
        pdu: Pdu,
    },
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

/// A service class user or a provider.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum ServiceClassRole {
    /// Service Class User
    Scu,
    /// Service Class Provider
    Scp,
}

#[derive(Debug)]
pub struct Association {
    service_class_type: ServiceClassRole,
    /// The accorded abstract syntax UID
    abstract_syntax_uid: String,
    /// The accorded transfer syntax UID
    transfer_syntax_uid: String,
    /// The identifier of the accorded presentation context
    presentation_context_id: u8,
    /// The maximum PDU length
    max_pdu_length: u32,
    /// The TCP stream to the other DICOM node
    socket: TcpStream,
}

impl Association {
    /// Retrieve the identifier of the negotiated presentation context.
    pub fn presentation_context_id(&self) -> u8 {
        self.presentation_context_id
    }

    /// Retrieve the negotiated abstract syntax UID.
    pub fn abstract_syntax_uid(&self) -> &str {
        &self.abstract_syntax_uid
    }

    /// Retrieve the negotiated transfer syntax UID.
    pub fn transfer_syntax_uid(&self) -> &str {
        &self.transfer_syntax_uid
    }

    /// Send a PDU message to the other intervenient.
    pub fn send(&mut self, msg: &Pdu) -> Result<()> {
        write_pdu(&mut self.socket, &msg).context(Send)
    }

    /// Read a PDU message from the other intervenient.
    pub fn receive(&mut self) -> Result<Pdu> {
        read_pdu(&mut self.socket, self.max_pdu_length).context(Receive)
    }

    /// Gracefully release the association.
    pub fn release(&mut self) -> Result<()> {
        write_pdu(&mut self.socket, &Pdu::ReleaseRQ).context(Send)?;

        let pdu = read_pdu(&mut self.socket, self.max_pdu_length).context(Receive)?;

        match pdu {
            Pdu::ReleaseRP => {}
            pdu @ Pdu::AbortRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::PData { .. }
            | pdu @ Pdu::ReleaseRQ { .. } => return UnexpectedResponse { pdu }.fail(),
            pdu @ Pdu::Unknown { .. } => return UnknownResponse { pdu }.fail(),
        }

        let _ = self.socket.shutdown(std::net::Shutdown::Both);
        Ok(())
    }
}

impl Drop for Association {
    fn drop(&mut self) {
        if self.service_class_type == ServiceClassRole::Scu {
            let _ = self.release();
        }
    }
}
<commit_msg>[ul] Improve association documentation, minor tweaks<commit_after>//! DICOM association module
//!
//! This module contains utilities for establishing associations
//! between DICOM nodes via TCP/IP.
//!
//! # Examples
//!
//! As a service class user (SCU) initiating communication,
//! a new association can be started via the [`ScuAssociationOptions`] type.
//! The minimum required properties are the accepted abstract syntaxes
//! and the TCP socket address to the target service class provider (SCP).
//!
//! [`ScuAssociationOptions`]: crate::association::scu::ScuAssociationOptions
//!

use std::net::TcpStream;

use snafu::{ResultExt, Snafu};

use crate::pdu::{reader::read_pdu, writer::write_pdu, Pdu};

pub mod scp;
pub mod scu;

#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum Error {
    /// failed to send PDU message
    Send { source: crate::pdu::writer::Error },

    /// failed to receive PDU message
    Receive { source: crate::pdu::reader::Error },

    #[snafu(display("unexpected response `{:?}`", pdu))]
    #[non_exhaustive]
    UnexpectedResponse {
        /// the PDU obtained from the other node
        pdu: Pdu,
    },

    #[snafu(display("unknown response  `{:?}`", pdu))]
    #[non_exhaustive]
    UnknownResponse {
        /// the PDU obtained from the other node, of variant Unknown
        pdu: Pdu,
    },
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

/// A service class user or a provider.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum ServiceClassRole {
    /// Service Class User
    Scu,
    /// Service Class Provider
    Scp,
}

#[derive(Debug)]
pub struct Association {
    service_class_role: ServiceClassRole,
    /// The accorded abstract syntax UID
    abstract_syntax_uid: String,
    /// The accorded transfer syntax UID
    transfer_syntax_uid: String,
    /// The identifier of the accorded presentation context
    presentation_context_id: u8,
    /// The maximum PDU length
    max_pdu_length: u32,
    /// The TCP stream to the other DICOM node
    socket: TcpStream,
}

impl Association {
    /// Retrieve the identifier of the negotiated presentation context.
    pub fn presentation_context_id(&self) -> u8 {
        self.presentation_context_id
    }

    /// Retrieve the negotiated abstract syntax UID.
    pub fn abstract_syntax_uid(&self) -> &str {
        &self.abstract_syntax_uid
    }

    /// Retrieve the negotiated transfer syntax UID.
    pub fn transfer_syntax_uid(&self) -> &str {
        &self.transfer_syntax_uid
    }

    /// Send a PDU message to the other intervenient.
    pub fn send(&mut self, msg: &Pdu) -> Result<()> {
        write_pdu(&mut self.socket, &msg).context(Send)
    }

    /// Read a PDU message from the other intervenient.
    pub fn receive(&mut self) -> Result<Pdu> {
        read_pdu(&mut self.socket, self.max_pdu_length).context(Receive)
    }

    /// Gracefully release the association.
    pub fn release(&mut self) -> Result<()> {
        write_pdu(&mut self.socket, &Pdu::ReleaseRQ).context(Send)?;

        let pdu = read_pdu(&mut self.socket, self.max_pdu_length).context(Receive)?;

        match pdu {
            Pdu::ReleaseRP => {}
            pdu @ Pdu::AbortRQ { .. }
            | pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::AssociationRQ { .. }
            | pdu @ Pdu::PData { .. }
            | pdu @ Pdu::ReleaseRQ { .. } => return UnexpectedResponse { pdu }.fail(),
            pdu @ Pdu::Unknown { .. } => return UnknownResponse { pdu }.fail(),
        }

        let _ = self.socket.shutdown(std::net::Shutdown::Both);
        Ok(())
    }
}

impl Drop for Association {
    fn drop(&mut self) {
        if self.service_class_role == ServiceClassRole::Scu {
            let _ = self.release();
        }
    }
}
<|endoftext|>


<commit_before>use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
use dicom_transfer_syntax_registry::TransferSyntaxRegistry;

/// Check that a transfer syntax repository
/// supports the given transfer syntax,
/// meaning that it can parse and decode DICOM data sets.
///
/// ```
/// # use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
/// # use dicom_ul::association::scp::is_supported_with_repo;
/// // Implicit VR Little Endian is guaranteed to be supported
/// assert!(is_supported_with_repo(TransferSyntaxRegistry, "1.2.840.10008.1.2"));
/// ```
pub fn is_supported_with_repo<R>(ts_repo: R, ts_uid: &str) -> bool
where
    R: TransferSyntaxIndex,
{
    ts_repo.get(ts_uid).filter(|ts| !ts.unsupported()).is_some()
}

/// Check that the main transfer syntax registry
/// supports the given transfer syntax,
/// meaning that it can parse and decode DICOM data sets.
///
/// ```
/// # use dicom_ul::association::scp::is_supported;
/// // Implicit VR Little Endian is guaranteed to be supported
/// assert!(is_supported("1.2.840.10008.1.2"));
/// ```
pub fn is_supported(ts_uid: &str) -> bool {
    is_supported_with_repo(TransferSyntaxRegistry, ts_uid)
}

/// From a sequence of transfer syntaxes,
/// choose the first transfer syntax to be supported
/// by the given transfer syntax repository.
pub fn choose_supported_with_repo<R, I, T>(ts_repo: R, it: I) -> Option<T>
where
    R: TransferSyntaxIndex,
    I: IntoIterator<Item = T>,
    T: AsRef<str>,
{
    it.into_iter()
        .find(|ts| is_supported_with_repo(&ts_repo, ts.as_ref()))
}

/// From a sequence of transfer syntaxes,
/// choose the first transfer syntax to be supported
/// by the main transfer syntax registry.
pub fn choose_supported<I, T>(it: I) -> Option<T>
where
    I: IntoIterator<Item = T>,
    T: AsRef<str>,
{
    it.into_iter().find(|ts| is_supported(ts.as_ref()))
}

#[cfg(test)]
mod tests {
    use super::choose_supported;

    #[test]
    fn test_choose_supported() {
        assert_eq!(choose_supported(vec!["1.1.1.1.1"]), None,);

        // string slices, impl VR first
        assert_eq!(
            choose_supported(vec!["1.2.840.10008.1.2", "1.2.840.10008.1.2.1"]),
            Some("1.2.840.10008.1.2"),
        );

        // heap allocated strings slices, expl VR first
        assert_eq!(
            choose_supported(vec![
                "1.2.840.10008.1.2.1".to_string(),
                "1.2.840.10008.1.2".to_string()
            ]),
            Some("1.2.840.10008.1.2.1".to_string()),
        );
    }
}
<commit_msg>[ul] More content in association::scp<commit_after>use std::{borrow::Cow, net::TcpStream};

use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use snafu::{ensure, ResultExt, Snafu};

use crate::pdu::{
    reader::read_pdu, writer::write_pdu, AssociationRJResult, AssociationRJServiceUserReason,
    AssociationRJSource, Pdu, PresentationContextResult, PresentationContextResultReason,
};

#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum Error {
    /// missing at least one abstract syntax to accept negotiations
    MissingAbstractSyntax,

    /// failed to receive association request
    ReceiveRequest { source: crate::pdu::reader::Error },

    /// failed to send association response
    SendResponse { source: crate::pdu::writer::Error },

    /// failed to send PDU
    Send { source: crate::pdu::writer::Error },

    /// failed to receive PDU
    Receive { source: crate::pdu::reader::Error },

    #[snafu(display("unexpected request from SCU `{:?}`", pdu))]
    #[non_exhaustive]
    UnexpectedRequest {
        /// the PDU obtained from the server
        pdu: Pdu,
    },

    #[snafu(display("unknown request from SCU `{:?}`", pdu))]
    #[non_exhaustive]
    UnknownRequest {
        /// the PDU obtained from the server, of variant Unknown
        pdu: Pdu,
    },

    /// association rejected
    Rejected,

    /// association aborted
    Aborted,
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Common interface for application entity access control policies.
pub trait AccessControl {
    /// Obtain the decision of whether to accept an incoming association request
    /// based on the recorded application entity titles.
    ///
    /// Returns Ok(()) if the SCU should be given clearance.
    /// Otherwise, a concrete association RJ service user reason is given.
    fn check_access(
        &self,
        this_ae_title: &str,
        calling_ae_title: &str,
        called_ae_title: &str,
    ) -> Result<(), AssociationRJServiceUserReason>;
}

/// An access control rule that accepts any incoming association request.
#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
pub struct AcceptAny;

impl AccessControl for AcceptAny {
    fn check_access(
        &self,
        _this_ae_title: &str,
        _calling_ae_title: &str,
        _called_ae_title: &str,
    ) -> Result<(), AssociationRJServiceUserReason> {
        Ok(())
    }
}

/// An access control rule that accepts association requests
/// that match the called AE title with the node's AE title.
#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
pub struct AcceptCalledAeTitle;

impl AccessControl for AcceptCalledAeTitle {
    fn check_access(
        &self,
        this_ae_title: &str,
        _calling_ae_title: &str,
        called_ae_title: &str,
    ) -> Result<(), AssociationRJServiceUserReason> {
        if this_ae_title == called_ae_title {
            Ok(())
        } else {
            Err(AssociationRJServiceUserReason::CalledAETitleNotRecognized)
        }
    }
}

/// A DICOM association builder for a service class provider (SCP).
///
/// This is the standard way of establishing an [`Association`]
/// with a requesting service class user (SCU).
/// Unlike the [`ScpAssociationOptions`],
/// a value of this type can be reused for multiple connections.
///
/// [`Association`]: crate::association::Association
///
/// # Example
///
/// ```no_run
/// # use std::net::TcpListener;
/// # use dicom_ul::association::scp::ScpAssociationOptions;
/// # fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let tcp_listener: TcpListener = unimplemented!();
/// let scp_options = ScpAssociationOptions::new()
///    .with_abstract_syntax("1.2.840.10008.1.1")
///    .with_transfer_syntax("1.2.840.10008.1.2.1");
///
/// let (stream, _address) = tcp_listener.accept()?;
/// scp_options.establish(stream)?;
/// # Ok(())
/// # }
/// ```
///
/// The SCP will by default accept all transfer syntaxes
/// supported by the main [transfer syntax registry][1].
///
/// Access control logic is provided through
///
/// [1]: dicom_transfer_syntax_registry
#[derive(Debug, Clone)]
pub struct ScpAssociationOptions<A> {
    /// the application entity access control policy
    ae_access_control: A,
    /// the AE title of this
    ae_title: Cow<'static, str>,
    /// the requested application context name
    application_context_name: Cow<'static, str>,
    /// the list of requested abstract syntaxes
    abstract_syntax_uids: Vec<Cow<'static, str>>,
    /// the list of requested transfer syntaxes
    transfer_syntax_uids: Vec<Cow<'static, str>>,
    /// the expected protocol version
    protocol_version: u16,
    /// the maximum PDU length
    max_pdu_length: u32,
}

impl Default for ScpAssociationOptions<AcceptAny> {
    fn default() -> Self {
        ScpAssociationOptions {
            ae_access_control: AcceptAny,
            ae_title: "THIS-SCP".into(),
            application_context_name: "1.2.840.10008.3.1.1.1".into(),
            abstract_syntax_uids: Vec::new(),
            transfer_syntax_uids: Vec::new(),
            protocol_version: 1,
            max_pdu_length: crate::pdu::reader::DEFAULT_MAX_PDU,
        }
    }
}

impl ScpAssociationOptions<AcceptAny> {
    /// Create a new set of options for establishing an association.
    pub fn new() -> Self {
        Self::default()
    }
}

impl<A> ScpAssociationOptions<A>
where
    A: AccessControl,
{
    /// Change the access control policy to accept any association
    /// regardless of the specified AE titles.
    ///
    /// This is the default behavior when the options are first created.
    pub fn accept_any(self) -> ScpAssociationOptions<AcceptAny> {
        self.ae_access_control(AcceptAny)
    }

    /// Change the access control policy to accept an association
    /// if the called AE title matches this node's AE title.
    ///
    /// The default is to accept any SCU
    /// regardless of the specified AE titles.
    pub fn accept_called_ae_title(self) -> ScpAssociationOptions<AcceptCalledAeTitle> {
        self.ae_access_control(AcceptCalledAeTitle)
    }

    /// Change the access control policy.
    ///
    /// The default is to accept any SCU
    /// regardless of the specified AE titles.
    pub fn ae_access_control<P>(self, access_control: P) -> ScpAssociationOptions<P> {
        let ScpAssociationOptions {
            ae_title,
            application_context_name,
            abstract_syntax_uids,
            transfer_syntax_uids,
            protocol_version,
            max_pdu_length,
            ..
        } = self;

        ScpAssociationOptions {
            ae_access_control: access_control,
            ae_title,
            application_context_name,
            abstract_syntax_uids,
            transfer_syntax_uids,
            protocol_version,
            max_pdu_length,
        }
    }

    /// Define the application entity title referring to this DICOM node.
    ///
    /// The default is `THIS-SCP`.
    pub fn ae_title<T>(mut self, ae_title: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.ae_title = ae_title.into();
        self
    }

    /// Include this abstract syntax
    /// in the list of proposed presentation contexts.
    pub fn with_abstract_syntax<T>(mut self, abstract_syntax_uid: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.abstract_syntax_uids.push(abstract_syntax_uid.into());
        self
    }

    /// Include this transfer syntax in each proposed presentation context.
    pub fn with_transfer_syntax<T>(mut self, transfer_syntax_uid: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.transfer_syntax_uids.push(transfer_syntax_uid.into());
        self
    }

    /// Override the maximum expected PDU length.
    pub fn max_pdu_length(mut self, value: u32) -> Self {
        self.max_pdu_length = value;
        self
    }

    /// Negotiate an association with the given TCP stream.
    pub fn establish(&self, mut socket: TcpStream) -> Result<ScpAssociation> {
        ensure!(!self.abstract_syntax_uids.is_empty(), MissingAbstractSyntax);

        let max_pdu_length = self.max_pdu_length;

        let pdu = read_pdu(&mut socket, max_pdu_length).context(ReceiveRequest)?;

        match pdu {
            Pdu::AssociationRQ {
                protocol_version,
                calling_ae_title,
                called_ae_title,
                application_context_name,
                presentation_contexts,
                user_variables: _,
            } => {
                if protocol_version != self.protocol_version {
                    write_pdu(
                        &mut socket,
                        &Pdu::AssociationRJ {
                            result: AssociationRJResult::Permanent,
                            source: AssociationRJSource::ServiceUser(
                                AssociationRJServiceUserReason::NoReasonGiven,
                            ),
                        },
                    )
                    .context(SendResponse)?;
                    return Rejected.fail();
                }

                if application_context_name != self.application_context_name {
                    write_pdu(
                        &mut socket,
                        &Pdu::AssociationRJ {
                            result: AssociationRJResult::Permanent,
                            source: AssociationRJSource::ServiceUser(
                                AssociationRJServiceUserReason::ApplicationContextNameNotSupported,
                            ),
                        },
                    )
                    .context(SendResponse)?;
                    return Rejected.fail();
                }

                self.ae_access_control
                    .check_access(&self.ae_title, &calling_ae_title, &called_ae_title)
                    .map(Ok)
                    .unwrap_or_else(|reason| {
                        write_pdu(
                            &mut socket,
                            &Pdu::AssociationRJ {
                                result: AssociationRJResult::Permanent,
                                source: AssociationRJSource::ServiceUser(reason),
                            },
                        )
                        .context(SendResponse)?;
                        Rejected.fail()
                    })?;

                let presentation_contexts: Vec<_> = presentation_contexts
                    .into_iter()
                    .map(|pc| {
                        if !self
                            .abstract_syntax_uids
                            .contains(&Cow::from(pc.abstract_syntax))
                        {
                            return PresentationContextResult {
                                id: pc.id,
                                reason: PresentationContextResultReason::AbstractSyntaxNotSupported,
                                transfer_syntax: "1.2.840.10008.1.2".to_string(),
                            };
                        }

                        let mut reason = PresentationContextResultReason::Acceptance;
                        let transfer_syntax = choose_supported(pc.transfer_syntaxes)
                            .unwrap_or_else(|| {
                                reason =
                                    PresentationContextResultReason::TransferSyntaxesNotSupported;
                                "1.2.840.10008.1.2".to_string()
                            });

                        PresentationContextResult {
                            id: pc.id,
                            reason,
                            transfer_syntax,
                        }
                    })
                    .collect();

                write_pdu(
                    &mut socket,
                    &Pdu::AssociationAC {
                        protocol_version: self.protocol_version,
                        application_context_name,
                        presentation_contexts: presentation_contexts.clone(),
                        user_variables: vec![],
                    },
                )
                .context(SendResponse)?;

                Ok(ScpAssociation {
                    presentation_contexts,
                    max_pdu_length,
                    socket,
                })
            }
            Pdu::ReleaseRQ => {
                write_pdu(&mut socket, &Pdu::ReleaseRP).context(SendResponse)?;
                Aborted.fail()
            }
            pdu @ Pdu::AssociationAC { .. }
            | pdu @ Pdu::AssociationRJ { .. }
            | pdu @ Pdu::PData { .. }
            | pdu @ Pdu::ReleaseRP
            | pdu @ Pdu::AbortRQ { .. } => UnexpectedRequest { pdu }.fail(),
            pdu @ Pdu::Unknown { .. } => UnknownRequest { pdu }.fail(),
        }
    }
}

/// A DICOM upper level association from the perspective
/// of a service class provider (SCP).
#[derive(Debug)]
pub struct ScpAssociation {
    /// The accorded presentation contexts
    presentation_contexts: Vec<PresentationContextResult>,
    /// The maximum PDU length
    max_pdu_length: u32,
    /// The TCP stream to the other DICOM node
    socket: TcpStream,
}

impl ScpAssociation {
    /// Send a PDU message to the other intervenient.
    pub fn send(&mut self, msg: &Pdu) -> Result<()> {
        write_pdu(&mut self.socket, &msg).context(Send)
    }

    /// Read a PDU message from the other intervenient.
    pub fn receive(&mut self) -> Result<Pdu> {
        read_pdu(&mut self.socket, self.max_pdu_length).context(Receive)
    }
}

/// Check that a transfer syntax repository
/// supports the given transfer syntax,
/// meaning that it can parse and decode DICOM data sets.
///
/// ```
/// # use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
/// # use dicom_ul::association::scp::is_supported_with_repo;
/// // Implicit VR Little Endian is guaranteed to be supported
/// assert!(is_supported_with_repo(TransferSyntaxRegistry, "1.2.840.10008.1.2"));
/// ```
pub fn is_supported_with_repo<R>(ts_repo: R, ts_uid: &str) -> bool
where
    R: TransferSyntaxIndex,
{
    ts_repo.get(ts_uid).filter(|ts| !ts.unsupported()).is_some()
}

/// Check that the main transfer syntax registry
/// supports the given transfer syntax,
/// meaning that it can parse and decode DICOM data sets.
///
/// ```
/// # use dicom_ul::association::scp::is_supported;
/// // Implicit VR Little Endian is guaranteed to be supported
/// assert!(is_supported("1.2.840.10008.1.2"));
/// ```
pub fn is_supported(ts_uid: &str) -> bool {
    is_supported_with_repo(TransferSyntaxRegistry, ts_uid)
}

/// From a sequence of transfer syntaxes,
/// choose the first transfer syntax to be supported
/// by the given transfer syntax repository.
pub fn choose_supported_with_repo<R, I, T>(ts_repo: R, it: I) -> Option<T>
where
    R: TransferSyntaxIndex,
    I: IntoIterator<Item = T>,
    T: AsRef<str>,
{
    it.into_iter()
        .find(|ts| is_supported_with_repo(&ts_repo, ts.as_ref()))
}

/// From a sequence of transfer syntaxes,
/// choose the first transfer syntax to be supported
/// by the main transfer syntax registry.
pub fn choose_supported<I, T>(it: I) -> Option<T>
where
    I: IntoIterator<Item = T>,
    T: AsRef<str>,
{
    it.into_iter().find(|ts| is_supported(ts.as_ref()))
}

#[cfg(test)]
mod tests {
    use super::choose_supported;

    #[test]
    fn test_choose_supported() {
        assert_eq!(choose_supported(vec!["1.1.1.1.1"]), None,);

        // string slices, impl VR first
        assert_eq!(
            choose_supported(vec!["1.2.840.10008.1.2", "1.2.840.10008.1.2.1"]),
            Some("1.2.840.10008.1.2"),
        );

        // heap allocated strings slices, expl VR first
        assert_eq!(
            choose_supported(vec![
                "1.2.840.10008.1.2.1".to_string(),
                "1.2.840.10008.1.2".to_string()
            ]),
            Some("1.2.840.10008.1.2.1".to_string()),
        );
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::MapErrInto;
use logger::ImagLogger;

use libimagstore::store::Store;

/// The Runtime object
///
/// This object contains the complete runtime environment of the imag application running.
#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /// Gets the CLI spec for the program and retreives the config file path (or uses the default on
    /// in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
    /// and builds the Runtime object with it.
    ///
    /// The cli_spec object should be initially build with the ::get_default_cli_builder() function.
    pub fn new(mut cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::io::stdout;

        use clap::Shell;

        use libimagerror::trace::trace_error;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.clone().get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");
        let colored      = !matches.is_present("no-color-output");

        Runtime::init_logger(is_debugging, is_verbose, colored);

        match matches.value_of(Runtime::arg_generate_compl()) {
            Some(shell) => {
                debug!("Generating shell completion script, writing to stdout");
                let shell   = shell.parse::<Shell>().unwrap(); // clap has our back here.
                let appname = String::from(cli_spec.get_name());
                cli_spec.gen_completions_to(appname, shell, &mut stdout());
            },
            _ => debug!("Not generating shell completion script"),
        }

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let configpath = matches.value_of("config")
                                .map_or_else(|| rtp.clone(), PathBuf::from);

        debug!("RTP path    = {:?}", rtp);
        debug!("Store path  = {:?}", storepath);
        debug!("Config path = {:?}", configpath);

        let cfg = match Configuration::new(&configpath) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(mut cfg) => {
                if let Err(e) = cfg.override_config(get_override_specs(&matches)) {
                    error!("Could not apply config overrides");
                    trace_error(&e);

                    // TODO: continue question (interactive)
                }

                Some(cfg)
            }
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath.clone(), store_config).map(|store| {
            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err_into(RuntimeErrorKind::Instantiate)
    }

    ///
    /// Get a commandline-interface builder object from `clap`
    ///
    /// This commandline interface builder object already contains some predefined interface flags:
    ///   * -v | --verbose for verbosity
    ///   * --debug for debugging
    ///   * -c <file> | --config <file> for alternative configuration file
    ///   * -r <path> | --rtp <path> for alternative runtimepath
    ///   * --store <path> for alternative store path
    /// Each has the appropriate help text included.
    ///
    /// The `appname` shall be "imag-<command>".
    ///
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name(Runtime::arg_verbosity_name())
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_debugging_name())
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_no_color_output_name())
                .long("no-color")
                .help("Disable color output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_config_name())
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_config_override_name())
                 .long("override-config")
                 .help("Override a configuration settings. Use 'key=value' pairs, where the key is a path in the TOML configuration. The value must be present in the configuration and be convertible to the type of the configuration setting. If the argument does not contain a '=', it gets ignored. Setting Arrays and Tables is not yet supported.")
                 .required(false)
                 .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_runtimepath_name())
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_storepath_name())
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_editor_name())
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_generate_compl())
                .long("generate-commandline-completion")
                .help("Generate the commandline completion for bash or zsh or fish")
                .required(false)
                .takes_value(true)
                .value_name("SHELL")
                .possible_values(&["bash", "fish", "zsh"]))

    }

    /// Get the argument names of the Runtime which are available
    pub fn arg_names() -> Vec<&'static str> {
        vec![
            Runtime::arg_verbosity_name(),
            Runtime::arg_debugging_name(),
            Runtime::arg_no_color_output_name(),
            Runtime::arg_config_name(),
            Runtime::arg_config_override_name(),
            Runtime::arg_runtimepath_name(),
            Runtime::arg_storepath_name(),
            Runtime::arg_editor_name(),
        ]
    }

    /// Get the verbosity argument name for the Runtime
    pub fn arg_verbosity_name() -> &'static str {
        "verbosity"
    }

    /// Get the debugging argument name for the Runtime
    pub fn arg_debugging_name() -> &'static str {
        "debugging"
    }

    /// Get the argument name for no color output of the Runtime
    pub fn arg_no_color_output_name() -> &'static str {
        "no-color-output"
    }

    /// Get the config argument name for the Runtime
    pub fn arg_config_name() -> &'static str {
        "config"
    }

    /// Get the config-override argument name for the Runtime
    pub fn arg_config_override_name() -> &'static str {
        "config-override"
    }

    /// Get the runtime argument name for the Runtime
    pub fn arg_runtimepath_name() -> &'static str {
        "runtimepath"
    }

    /// Get the storepath argument name for the Runtime
    pub fn arg_storepath_name() -> &'static str {
        "storepath"
    }

    /// Get the editor argument name for the Runtime
    pub fn arg_editor_name() -> &'static str {
        "editor"
    }

    /// Get the argument name for generating the completion
    pub fn arg_generate_compl() -> &'static str {
        "generate-completion"
    }

    /// Initialize the internal logger
    fn init_logger(is_debugging: bool, is_verbose: bool, colored: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Warn
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()).with_color(colored))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /// Get the verbosity flag value
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /// Get the debugging flag value
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /// Get the runtimepath
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /// Get the commandline interface matches
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /// Get the configuration object
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /// Get the store object
    pub fn store(&self) -> &Store {
        &self.store
    }

    /// Change the store backend to stdout
    ///
    /// For the documentation on purpose and cavecats, have a look at the documentation of the
    /// `Store::reset_backend()` function.
    ///
    pub fn store_backend_to_stdio(&mut self) -> Result<(), RuntimeError> {
        use libimagstore::file_abstraction::stdio::*;
        use libimagstore::file_abstraction::stdio::mapper::json::JsonMapper;
        use std::rc::Rc;
        use std::cell::RefCell;

        let mut input = ::std::io::empty();
        let output    = ::std::io::stdout();
        let output    = Rc::new(RefCell::new(output));
        let mapper    = JsonMapper::new();

        StdIoFileAbstraction::new(&mut input, output, mapper)
            .map_err_into(RuntimeErrorKind::Instantiate)
            .and_then(|backend| {
                self.store
                    .reset_backend(Box::new(backend))
                    .map_err_into(RuntimeErrorKind::Instantiate)
            })
    }

    /// Get a editor command object which can be called to open the $EDITOR
    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
    matches
        .values_of("config-override")
        .map(|values| {
             values
             .filter(|s| {
                 let b = s.contains("=");
                 if !b { warn!("override '{}' does not contain '=' - will be ignored!", s); }
                 b
             })
             .map(String::from)
             .collect()
        })
        .unwrap_or(vec![])
}

<commit_msg>Add Runtime::store_backend_to_stdout(), make _stdio() variant use stdin properly<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::MapErrInto;
use logger::ImagLogger;

use libimagstore::store::Store;

/// The Runtime object
///
/// This object contains the complete runtime environment of the imag application running.
#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /// Gets the CLI spec for the program and retreives the config file path (or uses the default on
    /// in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
    /// and builds the Runtime object with it.
    ///
    /// The cli_spec object should be initially build with the ::get_default_cli_builder() function.
    pub fn new(mut cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::io::stdout;

        use clap::Shell;

        use libimagerror::trace::trace_error;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.clone().get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");
        let colored      = !matches.is_present("no-color-output");

        Runtime::init_logger(is_debugging, is_verbose, colored);

        match matches.value_of(Runtime::arg_generate_compl()) {
            Some(shell) => {
                debug!("Generating shell completion script, writing to stdout");
                let shell   = shell.parse::<Shell>().unwrap(); // clap has our back here.
                let appname = String::from(cli_spec.get_name());
                cli_spec.gen_completions_to(appname, shell, &mut stdout());
            },
            _ => debug!("Not generating shell completion script"),
        }

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let configpath = matches.value_of("config")
                                .map_or_else(|| rtp.clone(), PathBuf::from);

        debug!("RTP path    = {:?}", rtp);
        debug!("Store path  = {:?}", storepath);
        debug!("Config path = {:?}", configpath);

        let cfg = match Configuration::new(&configpath) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(mut cfg) => {
                if let Err(e) = cfg.override_config(get_override_specs(&matches)) {
                    error!("Could not apply config overrides");
                    trace_error(&e);

                    // TODO: continue question (interactive)
                }

                Some(cfg)
            }
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath.clone(), store_config).map(|store| {
            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err_into(RuntimeErrorKind::Instantiate)
    }

    ///
    /// Get a commandline-interface builder object from `clap`
    ///
    /// This commandline interface builder object already contains some predefined interface flags:
    ///   * -v | --verbose for verbosity
    ///   * --debug for debugging
    ///   * -c <file> | --config <file> for alternative configuration file
    ///   * -r <path> | --rtp <path> for alternative runtimepath
    ///   * --store <path> for alternative store path
    /// Each has the appropriate help text included.
    ///
    /// The `appname` shall be "imag-<command>".
    ///
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name(Runtime::arg_verbosity_name())
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_debugging_name())
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_no_color_output_name())
                .long("no-color")
                .help("Disable color output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_config_name())
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_config_override_name())
                 .long("override-config")
                 .help("Override a configuration settings. Use 'key=value' pairs, where the key is a path in the TOML configuration. The value must be present in the configuration and be convertible to the type of the configuration setting. If the argument does not contain a '=', it gets ignored. Setting Arrays and Tables is not yet supported.")
                 .required(false)
                 .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_runtimepath_name())
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_storepath_name())
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_editor_name())
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_generate_compl())
                .long("generate-commandline-completion")
                .help("Generate the commandline completion for bash or zsh or fish")
                .required(false)
                .takes_value(true)
                .value_name("SHELL")
                .possible_values(&["bash", "fish", "zsh"]))

    }

    /// Get the argument names of the Runtime which are available
    pub fn arg_names() -> Vec<&'static str> {
        vec![
            Runtime::arg_verbosity_name(),
            Runtime::arg_debugging_name(),
            Runtime::arg_no_color_output_name(),
            Runtime::arg_config_name(),
            Runtime::arg_config_override_name(),
            Runtime::arg_runtimepath_name(),
            Runtime::arg_storepath_name(),
            Runtime::arg_editor_name(),
        ]
    }

    /// Get the verbosity argument name for the Runtime
    pub fn arg_verbosity_name() -> &'static str {
        "verbosity"
    }

    /// Get the debugging argument name for the Runtime
    pub fn arg_debugging_name() -> &'static str {
        "debugging"
    }

    /// Get the argument name for no color output of the Runtime
    pub fn arg_no_color_output_name() -> &'static str {
        "no-color-output"
    }

    /// Get the config argument name for the Runtime
    pub fn arg_config_name() -> &'static str {
        "config"
    }

    /// Get the config-override argument name for the Runtime
    pub fn arg_config_override_name() -> &'static str {
        "config-override"
    }

    /// Get the runtime argument name for the Runtime
    pub fn arg_runtimepath_name() -> &'static str {
        "runtimepath"
    }

    /// Get the storepath argument name for the Runtime
    pub fn arg_storepath_name() -> &'static str {
        "storepath"
    }

    /// Get the editor argument name for the Runtime
    pub fn arg_editor_name() -> &'static str {
        "editor"
    }

    /// Get the argument name for generating the completion
    pub fn arg_generate_compl() -> &'static str {
        "generate-completion"
    }

    /// Initialize the internal logger
    fn init_logger(is_debugging: bool, is_verbose: bool, colored: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Warn
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()).with_color(colored))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /// Get the verbosity flag value
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /// Get the debugging flag value
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /// Get the runtimepath
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /// Get the commandline interface matches
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /// Get the configuration object
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /// Get the store object
    pub fn store(&self) -> &Store {
        &self.store
    }

    /// Change the store backend to stdout
    ///
    /// For the documentation on purpose and cavecats, have a look at the documentation of the
    /// `Store::reset_backend()` function.
    ///
    pub fn store_backend_to_stdio(&mut self) -> Result<(), RuntimeError> {
        use libimagstore::file_abstraction::stdio::*;
        use libimagstore::file_abstraction::stdio::mapper::json::JsonMapper;
        use std::rc::Rc;
        use std::cell::RefCell;

        let mut input = ::std::io::stdin();
        let output    = ::std::io::stdout();
        let output    = Rc::new(RefCell::new(output));
        let mapper    = JsonMapper::new();

        StdIoFileAbstraction::new(&mut input, output, mapper)
            .map_err_into(RuntimeErrorKind::Instantiate)
            .and_then(|backend| {
                self.store
                    .reset_backend(Box::new(backend))
                    .map_err_into(RuntimeErrorKind::Instantiate)
            })
    }

    pub fn store_backend_to_stdout(&mut self) -> Result<(), RuntimeError> {
        use libimagstore::file_abstraction::stdio::mapper::json::JsonMapper;
        use libimagstore::file_abstraction::stdio::out::StdoutFileAbstraction;
        use std::rc::Rc;
        use std::cell::RefCell;

        let output    = ::std::io::stdout();
        let output    = Rc::new(RefCell::new(output));
        let mapper    = JsonMapper::new();

        StdoutFileAbstraction::new(output, mapper)
            .map_err_into(RuntimeErrorKind::Instantiate)
            .and_then(|backend| {
                self.store
                    .reset_backend(Box::new(backend))
                    .map_err_into(RuntimeErrorKind::Instantiate)
            })
    }

    /// Get a editor command object which can be called to open the $EDITOR
    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
    matches
        .values_of("config-override")
        .map(|values| {
             values
             .filter(|s| {
                 let b = s.contains("=");
                 if !b { warn!("override '{}' does not contain '=' - will be ignored!", s); }
                 b
             })
             .map(String::from)
             .collect()
        })
        .unwrap_or(vec![])
}

<|endoftext|>


<commit_before>//! JSON and serialization
//!
//! # What is JSON?
//!
//! JSON (JavaScript Object Notation) is a way to write data in JavaScript.  Like XML, it allows to
//! encode structured data in a text format that can be easily read by humans.  Its simple syntax
//! and native compatibility with JavaScript have made it a widely used format.
//!
//! Data types that can be encoded are JavaScript types (see the `serde_json:Value` enum for more
//! details):
//!
//! * `Boolean`: equivalent to rust's `bool`
//! * `I64`: equivalent to rust's `i64`
//! * `U64`: equivalent to rust's `u64`
//! * `F64`: equivalent to rust's `f64`
//! * `String`: equivalent to rust's `String`
//! * `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the
//!    same array
//! * `Object`: equivalent to rust's `BTreeMap<String, serde_json::Value>`; set the
//!    `preserve_order` feature to use `LinkedHashMap<String, serde_json::Value>` instead
//! * `Null`
//!
//! An object is a series of string keys mapping to values, in `"key": value` format.  Arrays are
//! enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).  A simple JSON
//! document encoding a person, his/her age, address and phone numbers could look like
//!
//! ```ignore
//! {
//!     "FirstName": "John",
//!     "LastName": "Doe",
//!     "Age": 43,
//!     "Address": {
//!         "Street": "Downing Street 10",
//!         "City": "London",
//!         "Country": "Great Britain"
//!     },
//!     "PhoneNumbers": [
//!         "+44 1234567",
//!         "+44 2345678"
//!     ]
//! }
//! ```
//!
//! If we assume that `FirstName` is optional and all other fields are mandatory, the above JSON could
//! correspond to the following Rust structs:
//!
//! ```ignore
//! #[derive(Serialize, Deserialize)]
//! struct Data {
//!     #[serde(rename="FirstName")] // to comply with Rust coding standards
//!     first_name: Option<String>,
//!     LastName: String,
//!     Age: u32,
//!     Address: Address,
//!     PhoneNumbers: Vec<String>
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Address {
//!     Street: String,
//!     City: String,
//!     Country: String
//! }
//! ```
//!
//! # Type-based Serialization and Deserialization
//!
//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
//! from JSON via the serialization API.  To be able to serialize a piece of data, it must implement
//! the `serde::Serialize` trait.  To be able to deserialize a piece of data, it must implement the
//! `serde::Deserialize` trait.  Serde provides provides an annotation to automatically generate
//! the code for these traits: `#[derive(Serialize, Deserialize)]`.
//!
//! The JSON API also provides an enum `serde_json::Value` and a method `to_value` to serialize
//! objects.  A `serde_json::Value` value can be serialized as a string or buffer using the
//! functions described above.  You can also use the `json::Serializer` object, which implements the
//! `Serializer` trait.
//!
//! # Examples of use
//!
//! ## Parsing a `str` to `Value` and reading the result
//!
//! ```rust
//! //#![feature(custom_derive, plugin)]
//! //#![plugin(serde_macros)]
//!
//! extern crate serde_json;
//!
//! use serde_json::Value;
//!
//! fn main() {
//!     let data: Value = serde_json::from_str("{\"foo\": 13, \"bar\": \"baz\"}").unwrap();
//!     println!("data: {:?}", data);
//!     // data: {"bar":"baz","foo":13}
//!     println!("object? {}", data.is_object());
//!     // object? true
//!
//!     let obj = data.as_object().unwrap();
//!     let foo = obj.get("foo").unwrap();
//!
//!     println!("array? {:?}", foo.as_array());
//!     // array? None
//!     println!("u64? {:?}", foo.as_u64());
//!     // u64? Some(13u64)
//!
//!     for (key, value) in obj.iter() {
//!         println!("{}: {}", key, match *value {
//!             Value::U64(v) => format!("{} (u64)", v),
//!             Value::String(ref v) => format!("{} (string)", v),
//!             _ => format!("other")
//!         });
//!     }
//!     // bar: baz (string)
//!     // foo: 13 (u64)
//! }
//! ```

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", deny(clippy, clippy_pedantic))]
// Because of "JavaScript"... fixed in Manishearth/rust-clippy#1071
#![cfg_attr(feature = "clippy", allow(doc_markdown))]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "clippy", allow(
// integer and float ser/de requires these sorts of casts
    cast_possible_truncation,
    cast_possible_wrap,
    cast_precision_loss,
    cast_sign_loss,
// string ser/de uses indexing and slicing
    indexing_slicing,
// things are often more readable this way
    shadow_reuse,
    shadow_unrelated,
    single_match_else,
    stutter,
))]

#![deny(missing_docs)]

extern crate num_traits;
extern crate core;
extern crate serde;
extern crate itoa;
extern crate dtoa;
#[cfg(feature = "preserve_order")]
extern crate linked_hash_map;

pub use self::de::{Deserializer, StreamDeserializer, from_iter, from_reader,
                   from_slice, from_str};
pub use self::error::{Error, ErrorCode, Result};
pub use self::ser::{Serializer, escape_str, to_string, to_string_pretty,
                    to_vec, to_vec_pretty, to_writer, to_writer_pretty};
pub use self::value::{Map, Value, from_value, to_value};

#[macro_use]
mod forward;

pub mod builder;
pub mod de;
pub mod error;
pub mod ser;
pub mod value;

mod read;
<commit_msg>Whitelist missing_docs_in_private_items<commit_after>//! JSON and serialization
//!
//! # What is JSON?
//!
//! JSON (JavaScript Object Notation) is a way to write data in JavaScript.  Like XML, it allows to
//! encode structured data in a text format that can be easily read by humans.  Its simple syntax
//! and native compatibility with JavaScript have made it a widely used format.
//!
//! Data types that can be encoded are JavaScript types (see the `serde_json:Value` enum for more
//! details):
//!
//! * `Boolean`: equivalent to rust's `bool`
//! * `I64`: equivalent to rust's `i64`
//! * `U64`: equivalent to rust's `u64`
//! * `F64`: equivalent to rust's `f64`
//! * `String`: equivalent to rust's `String`
//! * `Array`: equivalent to rust's `Vec<T>`, but also allowing objects of different types in the
//!    same array
//! * `Object`: equivalent to rust's `BTreeMap<String, serde_json::Value>`; set the
//!    `preserve_order` feature to use `LinkedHashMap<String, serde_json::Value>` instead
//! * `Null`
//!
//! An object is a series of string keys mapping to values, in `"key": value` format.  Arrays are
//! enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }).  A simple JSON
//! document encoding a person, his/her age, address and phone numbers could look like
//!
//! ```ignore
//! {
//!     "FirstName": "John",
//!     "LastName": "Doe",
//!     "Age": 43,
//!     "Address": {
//!         "Street": "Downing Street 10",
//!         "City": "London",
//!         "Country": "Great Britain"
//!     },
//!     "PhoneNumbers": [
//!         "+44 1234567",
//!         "+44 2345678"
//!     ]
//! }
//! ```
//!
//! If we assume that `FirstName` is optional and all other fields are mandatory, the above JSON could
//! correspond to the following Rust structs:
//!
//! ```ignore
//! #[derive(Serialize, Deserialize)]
//! struct Data {
//!     #[serde(rename="FirstName")] // to comply with Rust coding standards
//!     first_name: Option<String>,
//!     LastName: String,
//!     Age: u32,
//!     Address: Address,
//!     PhoneNumbers: Vec<String>
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Address {
//!     Street: String,
//!     City: String,
//!     Country: String
//! }
//! ```
//!
//! # Type-based Serialization and Deserialization
//!
//! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
//! from JSON via the serialization API.  To be able to serialize a piece of data, it must implement
//! the `serde::Serialize` trait.  To be able to deserialize a piece of data, it must implement the
//! `serde::Deserialize` trait.  Serde provides provides an annotation to automatically generate
//! the code for these traits: `#[derive(Serialize, Deserialize)]`.
//!
//! The JSON API also provides an enum `serde_json::Value` and a method `to_value` to serialize
//! objects.  A `serde_json::Value` value can be serialized as a string or buffer using the
//! functions described above.  You can also use the `json::Serializer` object, which implements the
//! `Serializer` trait.
//!
//! # Examples of use
//!
//! ## Parsing a `str` to `Value` and reading the result
//!
//! ```rust
//! //#![feature(custom_derive, plugin)]
//! //#![plugin(serde_macros)]
//!
//! extern crate serde_json;
//!
//! use serde_json::Value;
//!
//! fn main() {
//!     let data: Value = serde_json::from_str("{\"foo\": 13, \"bar\": \"baz\"}").unwrap();
//!     println!("data: {:?}", data);
//!     // data: {"bar":"baz","foo":13}
//!     println!("object? {}", data.is_object());
//!     // object? true
//!
//!     let obj = data.as_object().unwrap();
//!     let foo = obj.get("foo").unwrap();
//!
//!     println!("array? {:?}", foo.as_array());
//!     // array? None
//!     println!("u64? {:?}", foo.as_u64());
//!     // u64? Some(13u64)
//!
//!     for (key, value) in obj.iter() {
//!         println!("{}: {}", key, match *value {
//!             Value::U64(v) => format!("{} (u64)", v),
//!             Value::String(ref v) => format!("{} (string)", v),
//!             _ => format!("other")
//!         });
//!     }
//!     // bar: baz (string)
//!     // foo: 13 (u64)
//! }
//! ```

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", deny(clippy, clippy_pedantic))]
// Because of "JavaScript"... fixed in Manishearth/rust-clippy#1071
#![cfg_attr(feature = "clippy", allow(doc_markdown))]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "clippy", allow(
// integer and float ser/de requires these sorts of casts
    cast_possible_truncation,
    cast_possible_wrap,
    cast_precision_loss,
    cast_sign_loss,
// string ser/de uses indexing and slicing
    indexing_slicing,
// things are often more readable this way
    shadow_reuse,
    shadow_unrelated,
    single_match_else,
    stutter,
// not practical
    missing_docs_in_private_items,
))]

#![deny(missing_docs)]

extern crate num_traits;
extern crate core;
extern crate serde;
extern crate itoa;
extern crate dtoa;
#[cfg(feature = "preserve_order")]
extern crate linked_hash_map;

pub use self::de::{Deserializer, StreamDeserializer, from_iter, from_reader,
                   from_slice, from_str};
pub use self::error::{Error, ErrorCode, Result};
pub use self::ser::{Serializer, escape_str, to_string, to_string_pretty,
                    to_vec, to_vec_pretty, to_writer, to_writer_pretty};
pub use self::value::{Map, Value, from_value, to_value};

#[macro_use]
mod forward;

pub mod builder;
pub mod de;
pub mod error;
pub mod ser;
pub mod value;

mod read;
<|endoftext|>


<commit_before><commit_msg>feat: automatically add missing characters to xivdb<commit_after><|endoftext|>


<commit_before>use crate::hir::map::definitions::*;
use crate::hir::def_id::DefIndex;

use syntax::ast::*;
use syntax::ext::hygiene::ExpnId;
use syntax::visit;
use syntax::symbol::{kw, sym};
use syntax::parse::token::{self, Token};
use syntax_pos::Span;

/// Creates `DefId`s for nodes in the AST.
pub struct DefCollector<'a> {
    definitions: &'a mut Definitions,
    parent_def: DefIndex,
    expansion: ExpnId,
}

impl<'a> DefCollector<'a> {
    pub fn new(definitions: &'a mut Definitions, expansion: ExpnId) -> Self {
        let parent_def = definitions.invocation_parent(expansion);
        DefCollector { definitions, parent_def, expansion }
    }

    fn create_def(&mut self,
                  node_id: NodeId,
                  data: DefPathData,
                  span: Span)
                  -> DefIndex {
        let parent_def = self.parent_def;
        debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
        self.definitions.create_def_with_parent(parent_def, node_id, data, self.expansion, span)
    }

    pub fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: DefIndex, f: F) {
        let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def);
        f(self);
        self.parent_def = orig_parent_def;
    }

    fn visit_async_fn(
        &mut self,
        id: NodeId,
        name: Name,
        span: Span,
        header: &FnHeader,
        generics: &'a Generics,
        decl: &'a FnDecl,
        body: &'a Block,
    ) {
        let (closure_id, return_impl_trait_id) = match header.asyncness.node {
            IsAsync::Async {
                closure_id,
                return_impl_trait_id,
            } => (closure_id, return_impl_trait_id),
            _ => unreachable!(),
        };

        // For async functions, we need to create their inner defs inside of a
        // closure to match their desugared representation.
        let fn_def_data = DefPathData::ValueNs(name.as_interned_str());
        let fn_def = self.create_def(id, fn_def_data, span);
        return self.with_parent(fn_def, |this| {
            this.create_def(return_impl_trait_id, DefPathData::ImplTrait, span);

            visit::walk_generics(this, generics);
            visit::walk_fn_decl(this, decl);

            let closure_def = this.create_def(
                closure_id, DefPathData::ClosureExpr, span,
            );
            this.with_parent(closure_def, |this| {
                visit::walk_block(this, body);
            })
        })
    }

    pub fn visit_macro_invoc(&mut self, id: NodeId) {
        self.definitions.set_invocation_parent(id.placeholder_to_expn_id(), self.parent_def);
    }
}

impl<'a> visit::Visitor<'a> for DefCollector<'a> {
    fn visit_item(&mut self, i: &'a Item) {
        debug!("visit_item: {:?}", i);

        // Pick the def data. This need not be unique, but the more
        // information we encapsulate into, the better
        let def_data = match i.node {
            ItemKind::Impl(..) => DefPathData::Impl,
            ItemKind::Mod(..) if i.ident.name == kw::Invalid => {
                return visit::walk_item(self, i);
            }
            ItemKind::Mod(..) | ItemKind::Trait(..) | ItemKind::TraitAlias(..) |
            ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) |
            ItemKind::OpaqueTy(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) |
            ItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.as_interned_str()),
            ItemKind::Fn(
                ref decl,
                ref header,
                ref generics,
                ref body,
            ) if header.asyncness.node.is_async() => {
                return self.visit_async_fn(
                    i.id,
                    i.ident.name,
                    i.span,
                    header,
                    generics,
                    decl,
                    body,
                )
            }
            ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) =>
                DefPathData::ValueNs(i.ident.as_interned_str()),
            ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.as_interned_str()),
            ItemKind::Mac(..) => return self.visit_macro_invoc(i.id),
            ItemKind::GlobalAsm(..) => DefPathData::Misc,
            ItemKind::Use(..) => {
                return visit::walk_item(self, i);
            }
        };
        let def = self.create_def(i.id, def_data, i.span);

        self.with_parent(def, |this| {
            match i.node {
                ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => {
                    // If this is a unit or tuple-like struct, register the constructor.
                    if let Some(ctor_hir_id) = struct_def.ctor_id() {
                        this.create_def(ctor_hir_id, DefPathData::Ctor, i.span);
                    }
                }
                _ => {}
            }
            visit::walk_item(this, i);
        });
    }

    fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
        self.create_def(id, DefPathData::Misc, use_tree.span);
        visit::walk_use_tree(self, use_tree, id);
    }

    fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
        if let ForeignItemKind::Macro(_) = foreign_item.node {
            return self.visit_macro_invoc(foreign_item.id);
        }

        let def = self.create_def(foreign_item.id,
                                  DefPathData::ValueNs(foreign_item.ident.as_interned_str()),
                                  foreign_item.span);

        self.with_parent(def, |this| {
            visit::walk_foreign_item(this, foreign_item);
        });
    }

    fn visit_variant(&mut self, v: &'a Variant) {
        if v.is_placeholder {
            return self.visit_macro_invoc(v.id);
        }
        let def = self.create_def(v.id,
                                  DefPathData::TypeNs(v.ident.as_interned_str()),
                                  v.span);
        self.with_parent(def, |this| {
            if let Some(ctor_hir_id) = v.data.ctor_id() {
                this.create_def(ctor_hir_id, DefPathData::Ctor, v.span);
            }
            visit::walk_variant(this, v)
        });
    }

    fn visit_variant_data(&mut self, data: &'a VariantData) {
        // The assumption here is that non-`cfg` macro expansion cannot change field indices.
        // It currently holds because only inert attributes are accepted on fields,
        // and every such attribute expands into a single field after it's resolved.
        for (index, field) in data.fields().iter().enumerate() {
            if field.is_placeholder {
                self.visit_macro_invoc(field.id);
                self.definitions.placeholder_field_indices.insert(field.id, index);
                continue;
            }
            let name = field.ident.map(|ident| ident.name)
                .unwrap_or_else(|| sym::integer(index));
            let def = self.create_def(field.id,
                                      DefPathData::ValueNs(name.as_interned_str()),
                                      field.span);
            self.with_parent(def, |this| visit::walk_struct_field(this, field));
        }
    }

    fn visit_generic_param(&mut self, param: &'a GenericParam) {
        if param.is_placeholder {
            self.visit_macro_invoc(param.id);
            return;
        }
        let name = param.ident.as_interned_str();
        let def_path_data = match param.kind {
            GenericParamKind::Lifetime { .. } => DefPathData::LifetimeNs(name),
            GenericParamKind::Type { .. } => DefPathData::TypeNs(name),
            GenericParamKind::Const { .. } => DefPathData::ValueNs(name),
        };
        self.create_def(param.id, def_path_data, param.ident.span);

        visit::walk_generic_param(self, param);
    }

    fn visit_trait_item(&mut self, ti: &'a TraitItem) {
        let def_data = match ti.node {
            TraitItemKind::Method(..) | TraitItemKind::Const(..) =>
                DefPathData::ValueNs(ti.ident.as_interned_str()),
            TraitItemKind::Type(..) => {
                DefPathData::TypeNs(ti.ident.as_interned_str())
            },
            TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id),
        };

        let def = self.create_def(ti.id, def_data, ti.span);
        self.with_parent(def, |this| visit::walk_trait_item(this, ti));
    }

    fn visit_impl_item(&mut self, ii: &'a ImplItem) {
        let def_data = match ii.node {
            ImplItemKind::Method(MethodSig {
                ref header,
                ref decl,
            }, ref body) if header.asyncness.node.is_async() => {
                return self.visit_async_fn(
                    ii.id,
                    ii.ident.name,
                    ii.span,
                    header,
                    &ii.generics,
                    decl,
                    body,
                )
            }
            ImplItemKind::Method(..) | ImplItemKind::Const(..) =>
                DefPathData::ValueNs(ii.ident.as_interned_str()),
            ImplItemKind::TyAlias(..) |
            ImplItemKind::OpaqueTy(..) => {
                DefPathData::TypeNs(ii.ident.as_interned_str())
            },
            ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id),
        };

        let def = self.create_def(ii.id, def_data, ii.span);
        self.with_parent(def, |this| visit::walk_impl_item(this, ii));
    }

    fn visit_pat(&mut self, pat: &'a Pat) {
        match pat.node {
            PatKind::Mac(..) => return self.visit_macro_invoc(pat.id),
            _ => visit::walk_pat(self, pat),
        }
    }

    fn visit_anon_const(&mut self, constant: &'a AnonConst) {
        let def = self.create_def(constant.id,
                                  DefPathData::AnonConst,
                                  constant.value.span);
        self.with_parent(def, |this| visit::walk_anon_const(this, constant));
    }

    fn visit_expr(&mut self, expr: &'a Expr) {
        let parent_def = match expr.node {
            ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id),
            ExprKind::Closure(_, asyncness, ..) => {
                // Async closures desugar to closures inside of closures, so
                // we must create two defs.
                let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span);
                match asyncness {
                    IsAsync::Async { closure_id, .. } =>
                        self.create_def(closure_id, DefPathData::ClosureExpr, expr.span),
                    IsAsync::NotAsync => closure_def,
                }
            }
            ExprKind::Async(_, async_id, _) =>
                self.create_def(async_id, DefPathData::ClosureExpr, expr.span),
            _ => self.parent_def,
        };

        self.with_parent(parent_def, |this| visit::walk_expr(this, expr));
    }

    fn visit_ty(&mut self, ty: &'a Ty) {
        match ty.node {
            TyKind::Mac(..) => return self.visit_macro_invoc(ty.id),
            TyKind::ImplTrait(node_id, _) => {
                self.create_def(node_id, DefPathData::ImplTrait, ty.span);
            }
            _ => {}
        }
        visit::walk_ty(self, ty);
    }

    fn visit_stmt(&mut self, stmt: &'a Stmt) {
        match stmt.node {
            StmtKind::Mac(..) => self.visit_macro_invoc(stmt.id),
            _ => visit::walk_stmt(self, stmt),
        }
    }

    fn visit_token(&mut self, t: Token) {
        if let token::Interpolated(nt) = t.kind {
            if let token::NtExpr(ref expr) = *nt {
                if let ExprKind::Mac(..) = expr.node {
                    self.visit_macro_invoc(expr.id);
                }
            }
        }
    }

    fn visit_arm(&mut self, arm: &'a Arm) {
        if arm.is_placeholder {
            self.visit_macro_invoc(arm.id)
        } else {
            visit::walk_arm(self, arm)
        }
    }

    fn visit_field(&mut self, f: &'a Field) {
        if f.is_placeholder {
            self.visit_macro_invoc(f.id)
        } else {
            visit::walk_field(self, f)
        }
    }

    fn visit_field_pattern(&mut self, fp: &'a FieldPat) {
        if fp.is_placeholder {
            self.visit_macro_invoc(fp.id)
        } else {
            visit::walk_field_pattern(self, fp)
        }
    }

    fn visit_param(&mut self, p: &'a Param) {
        if p.is_placeholder {
            self.visit_macro_invoc(p.id)
        } else {
            visit::walk_param(self, p)
        }
    }

    // This method is called only when we are visiting an individual field
    // after expanding an attribute on it.
    fn visit_struct_field(&mut self, sf: &'a StructField) {
        if sf.is_placeholder {
            self.visit_macro_invoc(sf.id)
        } else {
            let name = sf.ident.map_or_else(
                || {
                    let expn_id = NodeId::placeholder_from_expn_id(self.expansion);
                    sym::integer(self.definitions.placeholder_field_indices[&expn_id])
                },
                |ident| ident.name,
            );
            let def = self.create_def(sf.id,
                                        DefPathData::ValueNs(name.as_interned_str()),
                                        sf.span);
            self.with_parent(def, |this| visit::walk_struct_field(this, sf));
        }
    }
}
<commit_msg>def_collector: Factor out common field handling code<commit_after>use crate::hir::map::definitions::*;
use crate::hir::def_id::DefIndex;

use syntax::ast::*;
use syntax::ext::hygiene::ExpnId;
use syntax::visit;
use syntax::symbol::{kw, sym};
use syntax::parse::token::{self, Token};
use syntax_pos::Span;

/// Creates `DefId`s for nodes in the AST.
pub struct DefCollector<'a> {
    definitions: &'a mut Definitions,
    parent_def: DefIndex,
    expansion: ExpnId,
}

impl<'a> DefCollector<'a> {
    pub fn new(definitions: &'a mut Definitions, expansion: ExpnId) -> Self {
        let parent_def = definitions.invocation_parent(expansion);
        DefCollector { definitions, parent_def, expansion }
    }

    fn create_def(&mut self,
                  node_id: NodeId,
                  data: DefPathData,
                  span: Span)
                  -> DefIndex {
        let parent_def = self.parent_def;
        debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
        self.definitions.create_def_with_parent(parent_def, node_id, data, self.expansion, span)
    }

    fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: DefIndex, f: F) {
        let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def);
        f(self);
        self.parent_def = orig_parent_def;
    }

    fn visit_async_fn(
        &mut self,
        id: NodeId,
        name: Name,
        span: Span,
        header: &FnHeader,
        generics: &'a Generics,
        decl: &'a FnDecl,
        body: &'a Block,
    ) {
        let (closure_id, return_impl_trait_id) = match header.asyncness.node {
            IsAsync::Async {
                closure_id,
                return_impl_trait_id,
            } => (closure_id, return_impl_trait_id),
            _ => unreachable!(),
        };

        // For async functions, we need to create their inner defs inside of a
        // closure to match their desugared representation.
        let fn_def_data = DefPathData::ValueNs(name.as_interned_str());
        let fn_def = self.create_def(id, fn_def_data, span);
        return self.with_parent(fn_def, |this| {
            this.create_def(return_impl_trait_id, DefPathData::ImplTrait, span);

            visit::walk_generics(this, generics);
            visit::walk_fn_decl(this, decl);

            let closure_def = this.create_def(
                closure_id, DefPathData::ClosureExpr, span,
            );
            this.with_parent(closure_def, |this| {
                visit::walk_block(this, body);
            })
        })
    }

    fn collect_field(&mut self, field: &'a StructField, index: Option<usize>) {
        if field.is_placeholder {
            self.visit_macro_invoc(field.id);
        } else {
            let name = field.ident.map(|ident| ident.name)
                .or_else(|| index.map(sym::integer))
                .unwrap_or_else(|| {
                    let node_id = NodeId::placeholder_from_expn_id(self.expansion);
                    sym::integer(self.definitions.placeholder_field_indices[&node_id])
                })
                .as_interned_str();
            let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span);
            self.with_parent(def, |this| visit::walk_struct_field(this, field));
        }
    }

    pub fn visit_macro_invoc(&mut self, id: NodeId) {
        self.definitions.set_invocation_parent(id.placeholder_to_expn_id(), self.parent_def);
    }
}

impl<'a> visit::Visitor<'a> for DefCollector<'a> {
    fn visit_item(&mut self, i: &'a Item) {
        debug!("visit_item: {:?}", i);

        // Pick the def data. This need not be unique, but the more
        // information we encapsulate into, the better
        let def_data = match i.node {
            ItemKind::Impl(..) => DefPathData::Impl,
            ItemKind::Mod(..) if i.ident.name == kw::Invalid => {
                return visit::walk_item(self, i);
            }
            ItemKind::Mod(..) | ItemKind::Trait(..) | ItemKind::TraitAlias(..) |
            ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) |
            ItemKind::OpaqueTy(..) | ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) |
            ItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.as_interned_str()),
            ItemKind::Fn(
                ref decl,
                ref header,
                ref generics,
                ref body,
            ) if header.asyncness.node.is_async() => {
                return self.visit_async_fn(
                    i.id,
                    i.ident.name,
                    i.span,
                    header,
                    generics,
                    decl,
                    body,
                )
            }
            ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) =>
                DefPathData::ValueNs(i.ident.as_interned_str()),
            ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.as_interned_str()),
            ItemKind::Mac(..) => return self.visit_macro_invoc(i.id),
            ItemKind::GlobalAsm(..) => DefPathData::Misc,
            ItemKind::Use(..) => {
                return visit::walk_item(self, i);
            }
        };
        let def = self.create_def(i.id, def_data, i.span);

        self.with_parent(def, |this| {
            match i.node {
                ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => {
                    // If this is a unit or tuple-like struct, register the constructor.
                    if let Some(ctor_hir_id) = struct_def.ctor_id() {
                        this.create_def(ctor_hir_id, DefPathData::Ctor, i.span);
                    }
                }
                _ => {}
            }
            visit::walk_item(this, i);
        });
    }

    fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
        self.create_def(id, DefPathData::Misc, use_tree.span);
        visit::walk_use_tree(self, use_tree, id);
    }

    fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
        if let ForeignItemKind::Macro(_) = foreign_item.node {
            return self.visit_macro_invoc(foreign_item.id);
        }

        let def = self.create_def(foreign_item.id,
                                  DefPathData::ValueNs(foreign_item.ident.as_interned_str()),
                                  foreign_item.span);

        self.with_parent(def, |this| {
            visit::walk_foreign_item(this, foreign_item);
        });
    }

    fn visit_variant(&mut self, v: &'a Variant) {
        if v.is_placeholder {
            return self.visit_macro_invoc(v.id);
        }
        let def = self.create_def(v.id,
                                  DefPathData::TypeNs(v.ident.as_interned_str()),
                                  v.span);
        self.with_parent(def, |this| {
            if let Some(ctor_hir_id) = v.data.ctor_id() {
                this.create_def(ctor_hir_id, DefPathData::Ctor, v.span);
            }
            visit::walk_variant(this, v)
        });
    }

    fn visit_variant_data(&mut self, data: &'a VariantData) {
        // The assumption here is that non-`cfg` macro expansion cannot change field indices.
        // It currently holds because only inert attributes are accepted on fields,
        // and every such attribute expands into a single field after it's resolved.
        for (index, field) in data.fields().iter().enumerate() {
            self.collect_field(field, Some(index));
            if field.is_placeholder && field.ident.is_none() {
                self.definitions.placeholder_field_indices.insert(field.id, index);
            }
        }
    }

    fn visit_generic_param(&mut self, param: &'a GenericParam) {
        if param.is_placeholder {
            self.visit_macro_invoc(param.id);
            return;
        }
        let name = param.ident.as_interned_str();
        let def_path_data = match param.kind {
            GenericParamKind::Lifetime { .. } => DefPathData::LifetimeNs(name),
            GenericParamKind::Type { .. } => DefPathData::TypeNs(name),
            GenericParamKind::Const { .. } => DefPathData::ValueNs(name),
        };
        self.create_def(param.id, def_path_data, param.ident.span);

        visit::walk_generic_param(self, param);
    }

    fn visit_trait_item(&mut self, ti: &'a TraitItem) {
        let def_data = match ti.node {
            TraitItemKind::Method(..) | TraitItemKind::Const(..) =>
                DefPathData::ValueNs(ti.ident.as_interned_str()),
            TraitItemKind::Type(..) => {
                DefPathData::TypeNs(ti.ident.as_interned_str())
            },
            TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id),
        };

        let def = self.create_def(ti.id, def_data, ti.span);
        self.with_parent(def, |this| visit::walk_trait_item(this, ti));
    }

    fn visit_impl_item(&mut self, ii: &'a ImplItem) {
        let def_data = match ii.node {
            ImplItemKind::Method(MethodSig {
                ref header,
                ref decl,
            }, ref body) if header.asyncness.node.is_async() => {
                return self.visit_async_fn(
                    ii.id,
                    ii.ident.name,
                    ii.span,
                    header,
                    &ii.generics,
                    decl,
                    body,
                )
            }
            ImplItemKind::Method(..) | ImplItemKind::Const(..) =>
                DefPathData::ValueNs(ii.ident.as_interned_str()),
            ImplItemKind::TyAlias(..) |
            ImplItemKind::OpaqueTy(..) => {
                DefPathData::TypeNs(ii.ident.as_interned_str())
            },
            ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id),
        };

        let def = self.create_def(ii.id, def_data, ii.span);
        self.with_parent(def, |this| visit::walk_impl_item(this, ii));
    }

    fn visit_pat(&mut self, pat: &'a Pat) {
        match pat.node {
            PatKind::Mac(..) => return self.visit_macro_invoc(pat.id),
            _ => visit::walk_pat(self, pat),
        }
    }

    fn visit_anon_const(&mut self, constant: &'a AnonConst) {
        let def = self.create_def(constant.id,
                                  DefPathData::AnonConst,
                                  constant.value.span);
        self.with_parent(def, |this| visit::walk_anon_const(this, constant));
    }

    fn visit_expr(&mut self, expr: &'a Expr) {
        let parent_def = match expr.node {
            ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id),
            ExprKind::Closure(_, asyncness, ..) => {
                // Async closures desugar to closures inside of closures, so
                // we must create two defs.
                let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span);
                match asyncness {
                    IsAsync::Async { closure_id, .. } =>
                        self.create_def(closure_id, DefPathData::ClosureExpr, expr.span),
                    IsAsync::NotAsync => closure_def,
                }
            }
            ExprKind::Async(_, async_id, _) =>
                self.create_def(async_id, DefPathData::ClosureExpr, expr.span),
            _ => self.parent_def,
        };

        self.with_parent(parent_def, |this| visit::walk_expr(this, expr));
    }

    fn visit_ty(&mut self, ty: &'a Ty) {
        match ty.node {
            TyKind::Mac(..) => return self.visit_macro_invoc(ty.id),
            TyKind::ImplTrait(node_id, _) => {
                self.create_def(node_id, DefPathData::ImplTrait, ty.span);
            }
            _ => {}
        }
        visit::walk_ty(self, ty);
    }

    fn visit_stmt(&mut self, stmt: &'a Stmt) {
        match stmt.node {
            StmtKind::Mac(..) => self.visit_macro_invoc(stmt.id),
            _ => visit::walk_stmt(self, stmt),
        }
    }

    fn visit_token(&mut self, t: Token) {
        if let token::Interpolated(nt) = t.kind {
            if let token::NtExpr(ref expr) = *nt {
                if let ExprKind::Mac(..) = expr.node {
                    self.visit_macro_invoc(expr.id);
                }
            }
        }
    }

    fn visit_arm(&mut self, arm: &'a Arm) {
        if arm.is_placeholder {
            self.visit_macro_invoc(arm.id)
        } else {
            visit::walk_arm(self, arm)
        }
    }

    fn visit_field(&mut self, f: &'a Field) {
        if f.is_placeholder {
            self.visit_macro_invoc(f.id)
        } else {
            visit::walk_field(self, f)
        }
    }

    fn visit_field_pattern(&mut self, fp: &'a FieldPat) {
        if fp.is_placeholder {
            self.visit_macro_invoc(fp.id)
        } else {
            visit::walk_field_pattern(self, fp)
        }
    }

    fn visit_param(&mut self, p: &'a Param) {
        if p.is_placeholder {
            self.visit_macro_invoc(p.id)
        } else {
            visit::walk_param(self, p)
        }
    }

    // This method is called only when we are visiting an individual field
    // after expanding an attribute on it.
    fn visit_struct_field(&mut self, field: &'a StructField) {
        self.collect_field(field, None);
    }
}
<|endoftext|>