<commit_before>#![crate_name = "simplemad_sys"]

extern crate libc;

use libc::{c_void, c_char, c_int, c_uint, c_ushort, c_long, uint16_t};
use std::fmt::{self, Result, Debug};
use std::ptr;

pub use libc::c_ulong;

#[link(name = "mad")]
extern {
    pub fn mad_decoder_init(decoder: *mut MadDecoder,
                            message: *mut c_void,
                            input_cb: extern fn(message: *mut c_void,
                                                stream: &MadStream) -> MadFlow,
                            header_cb: extern fn(message: *mut c_void,
                                                 header: &MadHeader) -> MadFlow,
                            filter_cb: extern fn(),
                            output_cb: extern fn(message: *mut c_void,
                                                 header: &MadHeader,
                                                 pcm: &MadPcm) -> MadFlow,
                            error_cb: extern fn(message: *mut c_void,
                                                stream: &MadStream,
                                                frame: *const c_void) -> MadFlow,
                            message_cb: extern fn());

    pub fn mad_decoder_run(decoder: &mut MadDecoder, mode: MadDecoderMode) -> c_int;
    pub fn mad_decoder_finish(decoder: &mut MadDecoder) -> c_int;
    pub fn mad_stream_buffer(stream: &MadStream, buf_start: *const u8, buf_len: c_ulong);

    pub fn mad_header_init(header: &mut MadHeader);
    pub fn mad_stream_init(stream: &mut MadStream);
    pub fn mad_frame_init(frame: &mut MadFrame);
    pub fn mad_synth_init(synth: &mut MadSynth);

    pub fn mad_stream_finish(stream: &mut MadStream);
    pub fn mad_frame_finish(frame: &mut MadFrame);

    pub fn mad_header_decode(header: &mut MadHeader, stream: &mut MadStream);
    pub fn mad_frame_decode(frame: &mut MadFrame, stream: &mut MadStream);
    pub fn mad_synth_frame(synth: &mut MadSynth, frame: &mut MadFrame);
}

/// libmad callbacks return MadFlow values, which are used to control the decoding process
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadFlow {
    /// continue normally
    Continue = 0x0000,

    /// stop decoding normally
    Stop = 0x0010,

    /// stop decoding and signal an error
    Break = 0x0011,

    /// ignore the current frame
    Ignore = 0x0020,
}

/// Errors generated by libmad
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadError {
    /// no error
    None = 0x0000,

    /// input buffer too small (or eof)
    BufLen = 0x0001,

    /// invalid (null) buffer pointer
    BufPtr = 0x0002,

    /// not enough memory
    NoMem = 0x0031,

    /// lost synchronization
    LostSync = 0x0101,

    /// reserved header layer value
    BadLayer = 0x0102,

    /// forbidden bitrate value
    BadBitRate = 0x0103,

    /// reserved sample frequency value
    BadSampleRate = 0x0104,

    /// reserved emphasis value
    BadEmphasis = 0x0105,

    /// crc check failed
    BadCRC = 0x0201,

    /// forbidden bit allocation value
    BadBitAlloc = 0x0211,

    /// bad scalefactor index
    BadScaleFactor = 0x0221,

    /// bad bitrate/mode combination
    BadMode = 0x0222,

    /// bad frame length
    BadFrameLen = 0x0231,

    /// bad big_values count
    BadBigValues = 0x0232,

    /// reserved block_type
    BadBlockType = 0x0233,

    /// bad scalefactor selection info
    BadScFSI = 0x0234,

    /// bad main_data_begin pointer
    BadDataPtr = 0x0235,

    /// bad audio data length
    BadPart3Len = 0x0236,

    /// bad huffman table select
    BadHuffTable = 0x0237,

    /// huffman data overrun
    BadHuffData = 0x0238,

    /// incompatible block_type for joint stereo
    BadStereo = 0x0239,
}

impl Default for MadError {
    fn default() -> MadError {
        MadError::None
    }
}

#[repr(C)]
pub struct MadBitPtr {
    pub byte: *mut c_char,
    pub cache: uint16_t,
    pub left: uint16_t,
}

impl Default for MadBitPtr {
    fn default() -> MadBitPtr {
        MadBitPtr {
            byte: ptr::null::<c_char>() as *mut c_char,
            cache: 0,
            left: 0,
        }
    }
}

#[repr(C)]
pub struct MadStream {
    pub buffer: *mut c_char,
    pub buff_end: *mut c_char,
    pub skip_len: c_ulong,
    pub sync: c_int,
    pub free_rate: c_ulong,
    pub this_frame: *mut c_char,
    pub next_frame: *mut c_char,
    pub ptr: MadBitPtr,
    pub anc_ptr: MadBitPtr,
    pub anc_bitlen: c_uint,
    pub buffer_mdlen: *mut c_char,
    pub md_len: c_uint,
    pub options: c_int,
    pub error: MadError,
}

impl Default for MadStream {
    fn default() -> MadStream {
        MadStream {
            buffer: ptr::null::<c_char>() as *mut c_char,
            buff_end: ptr::null::<c_char>() as *mut c_char,
            skip_len: 0,
            sync: 0,
            free_rate: 0,
            this_frame: ptr::null::<c_char>() as *mut c_char,
            next_frame: ptr::null::<c_char>() as *mut c_char,
            ptr: Default::default(),
            anc_ptr: Default::default(),
            anc_bitlen: 0,
            buffer_mdlen: ptr::null::<c_char>() as *mut c_char,
            md_len: 0,
            options: 0,
            error: MadError::None,
        }
    }
}

#[repr(C)]
pub struct MadFrame {
    pub header: MadHeader,
    pub options: c_int,
    pub sbsample: [[[i32; 32]; 36]; 2],
    pub overlap: *mut i32,
}

impl Default for MadFrame {
    fn default() -> MadFrame {
        MadFrame {
            header: Default::default(),
            options: 0,
            sbsample: [[[0; 32]; 36]; 2],
            overlap: ptr::null::<i32>() as *mut i32,
        }
    }
}

#[derive(Clone)]
#[repr(C)]
pub struct MadSynth {
    pub filter: [[[[[i32; 8]; 16]; 2]; 2]; 2],
    pub phase: c_uint,
    pub pcm: MadPcm,
}

impl Default for MadSynth {
    fn default() -> MadSynth {
        MadSynth {
            filter: [[[[[0; 8]; 16]; 2]; 2]; 2],
            phase: 0,
            pcm: Default::default(),
        }
    }
}

impl fmt::Debug for MadSynth {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "MadSynth {{phase: {}, pcm: {:?} }}",
               self.phase,
               self.pcm)
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadLayer {
    LayerI = 1,
    LayerII = 2,
    LayerIII = 3,
}

impl Default for MadLayer {
    fn default() -> MadLayer {
        MadLayer::LayerI
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadMode {
    SingleChannel = 0,
    DualChannel = 1,
    JointStereo = 2,
    Stereo = 3,
}

impl Default for MadMode {
    fn default() -> MadMode {
        MadMode::SingleChannel
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadEmphasis {
    None = 0,
    Fifty15Us = 1,
    CcittJ17 = 3,
    Reserved = 2,
}

impl Default for MadEmphasis {
    fn default() -> MadEmphasis {
        MadEmphasis::None
    }
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct MadTimer {
    pub seconds: c_long,
    pub fraction: c_ulong,
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct MadHeader {
    pub layer: MadLayer,
    pub mode: MadMode,
    pub mode_extension: c_int,
    pub emphasis: MadEmphasis,
    pub bit_rate: c_ulong,
    pub sample_rate: c_uint,
    pub crc_check: c_ushort,
    pub crc_target: c_ushort,
    pub flags: c_int,
    pub private_bits: c_int,
    pub duration: MadTimer,
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct MadPcm {
    pub sample_rate: c_uint,
    pub channels: uint16_t,
    pub length: uint16_t,
    pub samples: [[i32; 1152]; 2],
}

impl Default for MadPcm {
    fn default() -> MadPcm {
        MadPcm {
            sample_rate: 0,
            channels: 0,
            length: 0,
            samples: [[0; 1152]; 2],
        }
    }
}

impl fmt::Debug for MadPcm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "MadPcm {{sample_rate: {}, channels: {}, length: {}}}",
               self.sample_rate,
               self.channels,
               self.length)
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadDecoderMode {
    Sync = 0,
    Async = 1,
}

impl Default for MadDecoderMode {
    fn default() -> MadDecoderMode {
        MadDecoderMode::Sync
    }
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
struct MadAsyncParameters {
    pid: c_long,
    ain: c_int,
    aout: c_int,
}

#[repr(C)]
pub struct MadDecoder {
    mode: MadDecoderMode,
    options: c_int,
    async: MadAsyncParameters,
    sync: *const c_void,
    cb_data: *const c_void,
    input_func: *const c_void,
    header_func: *const c_void,
    filter_func: *const c_void,
    output_func: *const c_void,
    error_func: *const c_void,
    message_func: *const c_void,
}

impl Default for MadDecoder {
    fn default() -> MadDecoder {
        MadDecoder {
            mode: Default::default(),
            options: 0,
            async: Default::default(),
            sync: ptr::null::<c_void>(),
            cb_data: ptr::null::<c_void>(),
            input_func: ptr::null::<c_void>(),
            header_func: ptr::null::<c_void>(),
            filter_func: ptr::null::<c_void>(),
            output_func: ptr::null::<c_void>(),
            error_func: ptr::null::<c_void>(),
            message_func: ptr::null::<c_void>(),
        }
    }
}

#[cfg(test)]
mod test {
}
<commit_msg>Removed redundant linker flag<commit_after>#![crate_name = "simplemad_sys"]

extern crate libc;

use libc::{c_void, c_char, c_int, c_uint, c_ushort, c_long, uint16_t};
use std::fmt::{self, Result, Debug};
use std::ptr;

pub use libc::c_ulong;

extern {
    pub fn mad_decoder_init(decoder: *mut MadDecoder,
                            message: *mut c_void,
                            input_cb: extern fn(message: *mut c_void,
                                                stream: &MadStream) -> MadFlow,
                            header_cb: extern fn(message: *mut c_void,
                                                 header: &MadHeader) -> MadFlow,
                            filter_cb: extern fn(),
                            output_cb: extern fn(message: *mut c_void,
                                                 header: &MadHeader,
                                                 pcm: &MadPcm) -> MadFlow,
                            error_cb: extern fn(message: *mut c_void,
                                                stream: &MadStream,
                                                frame: *const c_void) -> MadFlow,
                            message_cb: extern fn());

    pub fn mad_decoder_run(decoder: &mut MadDecoder, mode: MadDecoderMode) -> c_int;
    pub fn mad_decoder_finish(decoder: &mut MadDecoder) -> c_int;
    pub fn mad_stream_buffer(stream: &MadStream, buf_start: *const u8, buf_len: c_ulong);

    pub fn mad_header_init(header: &mut MadHeader);
    pub fn mad_stream_init(stream: &mut MadStream);
    pub fn mad_frame_init(frame: &mut MadFrame);
    pub fn mad_synth_init(synth: &mut MadSynth);

    pub fn mad_stream_finish(stream: &mut MadStream);
    pub fn mad_frame_finish(frame: &mut MadFrame);

    pub fn mad_header_decode(header: &mut MadHeader, stream: &mut MadStream);
    pub fn mad_frame_decode(frame: &mut MadFrame, stream: &mut MadStream);
    pub fn mad_synth_frame(synth: &mut MadSynth, frame: &mut MadFrame);
}

/// libmad callbacks return MadFlow values, which are used to control the decoding process
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadFlow {
    /// continue normally
    Continue = 0x0000,

    /// stop decoding normally
    Stop = 0x0010,

    /// stop decoding and signal an error
    Break = 0x0011,

    /// ignore the current frame
    Ignore = 0x0020,
}

/// Errors generated by libmad
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadError {
    /// no error
    None = 0x0000,

    /// input buffer too small (or eof)
    BufLen = 0x0001,

    /// invalid (null) buffer pointer
    BufPtr = 0x0002,

    /// not enough memory
    NoMem = 0x0031,

    /// lost synchronization
    LostSync = 0x0101,

    /// reserved header layer value
    BadLayer = 0x0102,

    /// forbidden bitrate value
    BadBitRate = 0x0103,

    /// reserved sample frequency value
    BadSampleRate = 0x0104,

    /// reserved emphasis value
    BadEmphasis = 0x0105,

    /// crc check failed
    BadCRC = 0x0201,

    /// forbidden bit allocation value
    BadBitAlloc = 0x0211,

    /// bad scalefactor index
    BadScaleFactor = 0x0221,

    /// bad bitrate/mode combination
    BadMode = 0x0222,

    /// bad frame length
    BadFrameLen = 0x0231,

    /// bad big_values count
    BadBigValues = 0x0232,

    /// reserved block_type
    BadBlockType = 0x0233,

    /// bad scalefactor selection info
    BadScFSI = 0x0234,

    /// bad main_data_begin pointer
    BadDataPtr = 0x0235,

    /// bad audio data length
    BadPart3Len = 0x0236,

    /// bad huffman table select
    BadHuffTable = 0x0237,

    /// huffman data overrun
    BadHuffData = 0x0238,

    /// incompatible block_type for joint stereo
    BadStereo = 0x0239,
}

impl Default for MadError {
    fn default() -> MadError {
        MadError::None
    }
}

#[repr(C)]
pub struct MadBitPtr {
    pub byte: *mut c_char,
    pub cache: uint16_t,
    pub left: uint16_t,
}

impl Default for MadBitPtr {
    fn default() -> MadBitPtr {
        MadBitPtr {
            byte: ptr::null::<c_char>() as *mut c_char,
            cache: 0,
            left: 0,
        }
    }
}

#[repr(C)]
pub struct MadStream {
    pub buffer: *mut c_char,
    pub buff_end: *mut c_char,
    pub skip_len: c_ulong,
    pub sync: c_int,
    pub free_rate: c_ulong,
    pub this_frame: *mut c_char,
    pub next_frame: *mut c_char,
    pub ptr: MadBitPtr,
    pub anc_ptr: MadBitPtr,
    pub anc_bitlen: c_uint,
    pub buffer_mdlen: *mut c_char,
    pub md_len: c_uint,
    pub options: c_int,
    pub error: MadError,
}

impl Default for MadStream {
    fn default() -> MadStream {
        MadStream {
            buffer: ptr::null::<c_char>() as *mut c_char,
            buff_end: ptr::null::<c_char>() as *mut c_char,
            skip_len: 0,
            sync: 0,
            free_rate: 0,
            this_frame: ptr::null::<c_char>() as *mut c_char,
            next_frame: ptr::null::<c_char>() as *mut c_char,
            ptr: Default::default(),
            anc_ptr: Default::default(),
            anc_bitlen: 0,
            buffer_mdlen: ptr::null::<c_char>() as *mut c_char,
            md_len: 0,
            options: 0,
            error: MadError::None,
        }
    }
}

#[repr(C)]
pub struct MadFrame {
    pub header: MadHeader,
    pub options: c_int,
    pub sbsample: [[[i32; 32]; 36]; 2],
    pub overlap: *mut i32,
}

impl Default for MadFrame {
    fn default() -> MadFrame {
        MadFrame {
            header: Default::default(),
            options: 0,
            sbsample: [[[0; 32]; 36]; 2],
            overlap: ptr::null::<i32>() as *mut i32,
        }
    }
}

#[derive(Clone)]
#[repr(C)]
pub struct MadSynth {
    pub filter: [[[[[i32; 8]; 16]; 2]; 2]; 2],
    pub phase: c_uint,
    pub pcm: MadPcm,
}

impl Default for MadSynth {
    fn default() -> MadSynth {
        MadSynth {
            filter: [[[[[0; 8]; 16]; 2]; 2]; 2],
            phase: 0,
            pcm: Default::default(),
        }
    }
}

impl fmt::Debug for MadSynth {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "MadSynth {{phase: {}, pcm: {:?} }}",
               self.phase,
               self.pcm)
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadLayer {
    LayerI = 1,
    LayerII = 2,
    LayerIII = 3,
}

impl Default for MadLayer {
    fn default() -> MadLayer {
        MadLayer::LayerI
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MadMode {
    SingleChannel = 0,
    DualChannel = 1,
    JointStereo = 2,
    Stereo = 3,
}

impl Default for MadMode {
    fn default() -> MadMode {
        MadMode::SingleChannel
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadEmphasis {
    None = 0,
    Fifty15Us = 1,
    CcittJ17 = 3,
    Reserved = 2,
}

impl Default for MadEmphasis {
    fn default() -> MadEmphasis {
        MadEmphasis::None
    }
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct MadTimer {
    pub seconds: c_long,
    pub fraction: c_ulong,
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct MadHeader {
    pub layer: MadLayer,
    pub mode: MadMode,
    pub mode_extension: c_int,
    pub emphasis: MadEmphasis,
    pub bit_rate: c_ulong,
    pub sample_rate: c_uint,
    pub crc_check: c_ushort,
    pub crc_target: c_ushort,
    pub flags: c_int,
    pub private_bits: c_int,
    pub duration: MadTimer,
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct MadPcm {
    pub sample_rate: c_uint,
    pub channels: uint16_t,
    pub length: uint16_t,
    pub samples: [[i32; 1152]; 2],
}

impl Default for MadPcm {
    fn default() -> MadPcm {
        MadPcm {
            sample_rate: 0,
            channels: 0,
            length: 0,
            samples: [[0; 1152]; 2],
        }
    }
}

impl fmt::Debug for MadPcm {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "MadPcm {{sample_rate: {}, channels: {}, length: {}}}",
               self.sample_rate,
               self.channels,
               self.length)
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub enum MadDecoderMode {
    Sync = 0,
    Async = 1,
}

impl Default for MadDecoderMode {
    fn default() -> MadDecoderMode {
        MadDecoderMode::Sync
    }
}

#[derive(Default, Debug, Clone, Copy)]
#[repr(C)]
struct MadAsyncParameters {
    pid: c_long,
    ain: c_int,
    aout: c_int,
}

#[repr(C)]
pub struct MadDecoder {
    mode: MadDecoderMode,
    options: c_int,
    async: MadAsyncParameters,
    sync: *const c_void,
    cb_data: *const c_void,
    input_func: *const c_void,
    header_func: *const c_void,
    filter_func: *const c_void,
    output_func: *const c_void,
    error_func: *const c_void,
    message_func: *const c_void,
}

impl Default for MadDecoder {
    fn default() -> MadDecoder {
        MadDecoder {
            mode: Default::default(),
            options: 0,
            async: Default::default(),
            sync: ptr::null::<c_void>(),
            cb_data: ptr::null::<c_void>(),
            input_func: ptr::null::<c_void>(),
            header_func: ptr::null::<c_void>(),
            filter_func: ptr::null::<c_void>(),
            output_func: ptr::null::<c_void>(),
            error_func: ptr::null::<c_void>(),
            message_func: ptr::null::<c_void>(),
        }
    }
}

#[cfg(test)]
mod test {
}
<|endoftext|>


<commit_before>//! Diagnostic data structures.

#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::ops::Range;

/// A severity level for diagnostic messages.
///
/// These are ordered in the following way:
///
/// ```rust
/// use codespan_reporting::diagnostic::Severity;
///
/// assert!(Severity::Bug > Severity::Error);
/// assert!(Severity::Error > Severity::Warning);
/// assert!(Severity::Warning > Severity::Note);
/// assert!(Severity::Note > Severity::Help);
/// ```
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Severity {
    /// An unexpected bug.
    Bug,
    /// An error.
    Error,
    /// A warning.
    Warning,
    /// A note.
    Note,
    /// A help message.
    Help,
}

impl Severity {
    /// We want bugs to be the maximum severity, errors next, etc...
    fn to_cmp_int(self) -> u8 {
        match self {
            Severity::Bug => 5,
            Severity::Error => 4,
            Severity::Warning => 3,
            Severity::Note => 2,
            Severity::Help => 1,
        }
    }
}

impl PartialOrd for Severity {
    fn partial_cmp(&self, other: &Severity) -> Option<std::cmp::Ordering> {
        u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int())
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum LabelStyle {
    /// Labels that describe the primary cause of a diagnostic.
    Primary,
    /// Labels that provide additional context for a diagnostic.
    Secondary,
}

/// A label describing an underlined region of code associated with a diagnostic.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Label<FileId> {
    /// The style of the label.
    pub style: LabelStyle,
    /// The file that we are labelling.
    pub file_id: FileId,
    /// The range in bytes we are going to include in the final snippet.
    pub range: Range<usize>,
    /// An optional message to provide some additional information for the
    /// underlined code. These should not include line breaks.
    pub message: String,
}

impl<FileId> Label<FileId> {
    /// Create a new label.
    pub fn new(
        style: LabelStyle,
        file_id: FileId,
        range: impl Into<Range<usize>>,
    ) -> Label<FileId> {
        Label {
            style,
            file_id,
            range: range.into(),
            message: String::new(),
        }
    }

    /// Create a new label with a style of [`LabelStyle::Primary`].
    ///
    /// [`LabelStyle::Primary`]: LabelStyle::Primary
    pub fn primary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
        Label::new(LabelStyle::Primary, file_id, range)
    }

    /// Create a new label with a style of [`LabelStyle::Secondary`].
    ///
    /// [`LabelStyle::Secondary`]: LabelStyle::Secondary
    pub fn secondary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
        Label::new(LabelStyle::Secondary, file_id, range)
    }

    /// Add a message to the diagnostic.
    pub fn with_message(mut self, message: impl Into<String>) -> Label<FileId> {
        self.message = message.into();
        self
    }
}

/// Represents a diagnostic message that can provide information like errors and
/// warnings to the user.
///
/// The position of a Diagnostic is considered to be the position of the [`Label`] with a style of [`LabelStyle::primary`] that has the smallest start position.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Diagnostic<FileId> {
    /// The overall severity of the diagnostic
    pub severity: Severity,
    /// An optional code that identifies this diagnostic.
    pub code: Option<String>,
    /// The main message associated with this diagnostic.
    ///
    /// These should not include line breaks, and in order support the 'short'
    /// diagnostic display mod, the message should be specific enough to make
    /// sense on its own, without additional context provided by labels and notes.
    pub message: String,
    /// Source labels that describe the cause of the diagnostic.
    pub labels: Vec<Label<FileId>>,
    /// Notes that are associated with the primary cause of the diagnostic.
    /// These can include line breaks for improved formatting.
    pub notes: Vec<String>,
}

impl<FileId> Diagnostic<FileId> {
    /// Create a new diagnostic.
    pub fn new(severity: Severity) -> Diagnostic<FileId> {
        Diagnostic {
            severity,
            code: None,
            message: String::new(),
            labels: Vec::new(),
            notes: Vec::new(),
        }
    }

    /// Create a new diagnostic with a severity of [`Severity::Bug`].
    ///
    /// [`Severity::Bug`]: Severity::Bug
    pub fn bug() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Bug)
    }

    /// Create a new diagnostic with a severity of [`Severity::Error`].
    ///
    /// [`Severity::Error`]: Severity::Error
    pub fn error() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Error)
    }

    /// Create a new diagnostic with a severity of [`Severity::Warning`].
    ///
    /// [`Severity::Warning`]: Severity::Warning
    pub fn warning() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Warning)
    }

    /// Create a new diagnostic with a severity of [`Severity::Note`].
    ///
    /// [`Severity::Note`]: Severity::Note
    pub fn note() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Note)
    }

    /// Create a new diagnostic with a severity of [`Severity::Help`].
    ///
    /// [`Severity::Help`]: Severity::Help
    pub fn help() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Help)
    }

    /// Add an error code to the diagnostic.
    pub fn with_code(mut self, code: impl Into<String>) -> Diagnostic<FileId> {
        self.code = Some(code.into());
        self
    }

    /// Add a message to the diagnostic.
    pub fn with_message(mut self, message: impl Into<String>) -> Diagnostic<FileId> {
        self.message = message.into();
        self
    }

    /// Add some labels to the diagnostic.
    pub fn with_labels(mut self, labels: Vec<Label<FileId>>) -> Diagnostic<FileId> {
        self.labels = labels;
        self
    }

    /// Add some notes to the diagnostic.
    pub fn with_notes(mut self, notes: Vec<String>) -> Diagnostic<FileId> {
        self.notes = notes;
        self
    }
}
<commit_msg>add label order to documentation<commit_after>//! Diagnostic data structures.

#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::ops::Range;

/// A severity level for diagnostic messages.
///
/// These are ordered in the following way:
///
/// ```rust
/// use codespan_reporting::diagnostic::Severity;
///
/// assert!(Severity::Bug > Severity::Error);
/// assert!(Severity::Error > Severity::Warning);
/// assert!(Severity::Warning > Severity::Note);
/// assert!(Severity::Note > Severity::Help);
/// ```
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Severity {
    /// An unexpected bug.
    Bug,
    /// An error.
    Error,
    /// A warning.
    Warning,
    /// A note.
    Note,
    /// A help message.
    Help,
}

impl Severity {
    /// We want bugs to be the maximum severity, errors next, etc...
    fn to_cmp_int(self) -> u8 {
        match self {
            Severity::Bug => 5,
            Severity::Error => 4,
            Severity::Warning => 3,
            Severity::Note => 2,
            Severity::Help => 1,
        }
    }
}

impl PartialOrd for Severity {
    fn partial_cmp(&self, other: &Severity) -> Option<std::cmp::Ordering> {
        u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int())
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum LabelStyle {
    /// Labels that describe the primary cause of a diagnostic.
    Primary,
    /// Labels that provide additional context for a diagnostic.
    Secondary,
}

/// A label describing an underlined region of code associated with a diagnostic.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Label<FileId> {
    /// The style of the label.
    pub style: LabelStyle,
    /// The file that we are labelling.
    pub file_id: FileId,
    /// The range in bytes we are going to include in the final snippet.
    pub range: Range<usize>,
    /// An optional message to provide some additional information for the
    /// underlined code. These should not include line breaks.
    pub message: String,
}

impl<FileId> Label<FileId> {
    /// Create a new label.
    pub fn new(
        style: LabelStyle,
        file_id: FileId,
        range: impl Into<Range<usize>>,
    ) -> Label<FileId> {
        Label {
            style,
            file_id,
            range: range.into(),
            message: String::new(),
        }
    }

    /// Create a new label with a style of [`LabelStyle::Primary`].
    ///
    /// [`LabelStyle::Primary`]: LabelStyle::Primary
    pub fn primary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
        Label::new(LabelStyle::Primary, file_id, range)
    }

    /// Create a new label with a style of [`LabelStyle::Secondary`].
    ///
    /// [`LabelStyle::Secondary`]: LabelStyle::Secondary
    pub fn secondary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
        Label::new(LabelStyle::Secondary, file_id, range)
    }

    /// Add a message to the diagnostic.
    pub fn with_message(mut self, message: impl Into<String>) -> Label<FileId> {
        self.message = message.into();
        self
    }
}

/// Represents a diagnostic message that can provide information like errors and
/// warnings to the user.
///
/// The position of a Diagnostic is considered to be the position of the [`Label`] with a style of [`LabelStyle::primary`] that has the smallest start position.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Diagnostic<FileId> {
    /// The overall severity of the diagnostic
    pub severity: Severity,
    /// An optional code that identifies this diagnostic.
    pub code: Option<String>,
    /// The main message associated with this diagnostic.
    ///
    /// These should not include line breaks, and in order support the 'short'
    /// diagnostic display mod, the message should be specific enough to make
    /// sense on its own, without additional context provided by labels and notes.
    pub message: String,
    /// Source labels that describe the cause of the diagnostic.
    /// The order of the labels inside the vector does not have any meaning.
    /// The labels are always arranged in the order they appear in the source code.
    pub labels: Vec<Label<FileId>>,
    /// Notes that are associated with the primary cause of the diagnostic.
    /// These can include line breaks for improved formatting.
    pub notes: Vec<String>,
}

impl<FileId> Diagnostic<FileId> {
    /// Create a new diagnostic.
    pub fn new(severity: Severity) -> Diagnostic<FileId> {
        Diagnostic {
            severity,
            code: None,
            message: String::new(),
            labels: Vec::new(),
            notes: Vec::new(),
        }
    }

    /// Create a new diagnostic with a severity of [`Severity::Bug`].
    ///
    /// [`Severity::Bug`]: Severity::Bug
    pub fn bug() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Bug)
    }

    /// Create a new diagnostic with a severity of [`Severity::Error`].
    ///
    /// [`Severity::Error`]: Severity::Error
    pub fn error() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Error)
    }

    /// Create a new diagnostic with a severity of [`Severity::Warning`].
    ///
    /// [`Severity::Warning`]: Severity::Warning
    pub fn warning() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Warning)
    }

    /// Create a new diagnostic with a severity of [`Severity::Note`].
    ///
    /// [`Severity::Note`]: Severity::Note
    pub fn note() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Note)
    }

    /// Create a new diagnostic with a severity of [`Severity::Help`].
    ///
    /// [`Severity::Help`]: Severity::Help
    pub fn help() -> Diagnostic<FileId> {
        Diagnostic::new(Severity::Help)
    }

    /// Add an error code to the diagnostic.
    pub fn with_code(mut self, code: impl Into<String>) -> Diagnostic<FileId> {
        self.code = Some(code.into());
        self
    }

    /// Add a message to the diagnostic.
    pub fn with_message(mut self, message: impl Into<String>) -> Diagnostic<FileId> {
        self.message = message.into();
        self
    }

    /// Add some labels to the diagnostic.
    pub fn with_labels(mut self, labels: Vec<Label<FileId>>) -> Diagnostic<FileId> {
        self.labels = labels;
        self
    }

    /// Add some notes to the diagnostic.
    pub fn with_notes(mut self, notes: Vec<String>) -> Diagnostic<FileId> {
        self.notes = notes;
        self
    }
}
<|endoftext|>


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

//! Utilities for reading, writing, and transforming data for the LSP service
// We use two crates, lsp_types and lsp_server, for interacting with LSP. This module re-exports
// types from both so that we have a central source-of-truth for all LSP-related utilities.
use crate::lsp_process_error::LSPProcessResult;
use common::Location;
use crossbeam::crossbeam_channel::Sender;
pub use lsp_server::{Connection, Message};
pub use lsp_server::{
    Notification as ServerNotification, ProtocolError, Request as ServerRequest,
    RequestId as ServerRequestId, Response as ServerResponse,
};
pub use lsp_types::{notification::*, request::*, *};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub enum LSPBridgeMessage {
    GotoDefinitionRequest {
        request_id: ServerRequestId,
        text_document_position: TextDocumentPositionParams,
    },
    CompletionRequest {
        request_id: ServerRequestId,
        params: CompletionParams,
    },
    HoverRequest {
        request_id: ServerRequestId,
        text_document_position: TextDocumentPositionParams,
    },
    DidOpenTextDocument(DidOpenTextDocumentParams),
    DidChangeTextDocument(DidChangeTextDocumentParams),
    DidCloseTextDocument(DidCloseTextDocumentParams),
}

/// Converts a Location to a Url pointing to the canonical path based on the root_dir provided.
/// Returns None if we are unable to do the conversion
pub fn url_from_location(location: Location, root_dir: &PathBuf) -> Option<Url> {
    let file_path = location.source_location().path();
    let canonical_path = fs::canonicalize(root_dir.join(file_path)).ok()?;
    Url::from_file_path(canonical_path).ok()
}

#[derive(Debug)]
pub enum ShowStatus {}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowStatusParams {
    #[serde(rename = "type")]
    pub typ: MessageType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub progress: Option<Progress>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uri: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub short_message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub actions: Option<Vec<MessageActionItem>>,
}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Progress {
    numerator: f32,
    denominator: Option<f32>,
}

impl Request for ShowStatus {
    type Params = ShowStatusParams;
    type Result = ();
    const METHOD: &'static str = "window/showStatus";
}

pub fn set_ready_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: ready".into()),
        Some("Relay: ready".into()),
        MessageType::Info,
        sender,
    );
}

pub fn set_initializing_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: initializing".into()),
        Some("Relay: initializing".into()),
        MessageType::Warning,
        sender,
    );
}

pub fn set_running_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: compiling".into()),
        Some("Relay: compiling".into()),
        MessageType::Warning,
        sender,
    );
}

fn update_status(
    short_message: Option<String>,
    message: Option<String>,
    typ: MessageType,
    sender: &Sender<Message>,
) {
    let request = ServerRequest::new(
        ShowStatus::METHOD.to_string().into(),
        ShowStatus::METHOD.into(),
        ShowStatusParams {
            typ,
            progress: None,
            uri: None,
            message,
            short_message,
            actions: None,
        },
    );
    sender.send(Message::Request(request)).unwrap();
}

/// Show a notification in the client
#[allow(dead_code)]
pub fn show_info_message(
    message: impl Into<String>,
    connection: &Connection,
) -> LSPProcessResult<()> {
    let notif = ServerNotification::new(
        ShowMessage::METHOD.into(),
        ShowMessageParams {
            typ: MessageType::Info,
            message: message.into(),
        },
    );
    connection
        .sender
        .send(Message::Notification(notif))
        .unwrap_or_else(|_| {
            // TODO(brandondail) log here
        });
    Ok(())
}

/// Publish diagnostics to the client
pub fn publish_diagnostic(
    diagnostic_params: PublishDiagnosticsParams,
    sender: &Sender<Message>,
) -> LSPProcessResult<()> {
    let notif = ServerNotification::new(PublishDiagnostics::METHOD.into(), diagnostic_params);
    sender
        .send(Message::Notification(notif))
        .unwrap_or_else(|_| {
            // TODO(brandondail) log here
        });
    Ok(())
}
<commit_msg>Make show_info_message more useful<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for reading, writing, and transforming data for the LSP service
// We use two crates, lsp_types and lsp_server, for interacting with LSP. This module re-exports
// types from both so that we have a central source-of-truth for all LSP-related utilities.
use crate::lsp_process_error::LSPProcessResult;
use common::Location;
use crossbeam::{crossbeam_channel::Sender, SendError};
pub use lsp_server::{Connection, Message};
pub use lsp_server::{
    Notification as ServerNotification, ProtocolError, Request as ServerRequest,
    RequestId as ServerRequestId, Response as ServerResponse,
};
pub use lsp_types::{notification::*, request::*, *};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub enum LSPBridgeMessage {
    GotoDefinitionRequest {
        request_id: ServerRequestId,
        text_document_position: TextDocumentPositionParams,
    },
    CompletionRequest {
        request_id: ServerRequestId,
        params: CompletionParams,
    },
    HoverRequest {
        request_id: ServerRequestId,
        text_document_position: TextDocumentPositionParams,
    },
    DidOpenTextDocument(DidOpenTextDocumentParams),
    DidChangeTextDocument(DidChangeTextDocumentParams),
    DidCloseTextDocument(DidCloseTextDocumentParams),
}

/// Converts a Location to a Url pointing to the canonical path based on the root_dir provided.
/// Returns None if we are unable to do the conversion
pub fn url_from_location(location: Location, root_dir: &PathBuf) -> Option<Url> {
    let file_path = location.source_location().path();
    let canonical_path = fs::canonicalize(root_dir.join(file_path)).ok()?;
    Url::from_file_path(canonical_path).ok()
}

#[derive(Debug)]
pub enum ShowStatus {}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowStatusParams {
    #[serde(rename = "type")]
    pub typ: MessageType,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub progress: Option<Progress>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uri: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub short_message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub actions: Option<Vec<MessageActionItem>>,
}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Progress {
    numerator: f32,
    denominator: Option<f32>,
}

impl Request for ShowStatus {
    type Params = ShowStatusParams;
    type Result = ();
    const METHOD: &'static str = "window/showStatus";
}

pub fn set_ready_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: ready".into()),
        Some("Relay: ready".into()),
        MessageType::Info,
        sender,
    );
}

pub fn set_initializing_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: initializing".into()),
        Some("Relay: initializing".into()),
        MessageType::Warning,
        sender,
    );
}

pub fn set_running_status(sender: &Sender<Message>) {
    update_status(
        Some("Relay: compiling".into()),
        Some("Relay: compiling".into()),
        MessageType::Warning,
        sender,
    );
}

fn update_status(
    short_message: Option<String>,
    message: Option<String>,
    typ: MessageType,
    sender: &Sender<Message>,
) {
    let request = ServerRequest::new(
        ShowStatus::METHOD.to_string().into(),
        ShowStatus::METHOD.into(),
        ShowStatusParams {
            typ,
            progress: None,
            uri: None,
            message,
            short_message,
            actions: None,
        },
    );
    sender.send(Message::Request(request)).unwrap();
}

/// Show a notification in the client
#[allow(dead_code)]
pub fn show_info_message(
    sender: &Sender<Message>,
    message: impl Into<String>,
) -> Result<(), SendError<Message>> {
    let notif = ServerNotification::new(
        ShowMessage::METHOD.into(),
        ShowMessageParams {
            typ: MessageType::Info,
            message: message.into(),
        },
    );

    sender.send(Message::Notification(notif))
}

/// Publish diagnostics to the client
pub fn publish_diagnostic(
    diagnostic_params: PublishDiagnosticsParams,
    sender: &Sender<Message>,
) -> LSPProcessResult<()> {
    let notif = ServerNotification::new(PublishDiagnostics::METHOD.into(), diagnostic_params);
    sender
        .send(Message::Notification(notif))
        .unwrap_or_else(|_| {
            // TODO(brandondail) log here
        });
    Ok(())
}
<|endoftext|>


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

use std::env;
use std::ffi::OsString;

use common::ui::UI;
use hcore::crypto::CACHE_KEY_PATH_ENV_VAR;
use hcore::env as henv;
use hcore::fs::CACHE_KEY_PATH;
use hcore::os::users;

use config;
use error::Result;

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
    try!(inner::rerun_with_sudo_if_needed(ui));

    // If the `$HAB_ORIGIN` environment variable is not present, then see if a default is set in
    // the CLI config. If so, set it as the `$HAB_ORIGIN` environment variable for the `hab-studio`
    // or `docker` execv call.
    if henv::var("HAB_ORIGIN").is_err() {
        let config = try!(config::load_with_sudo_user());
        if let Some(default_origin) = config.origin {
            debug!("Setting default origin {} via CLI config", &default_origin);
            env::set_var("HAB_ORIGIN", default_origin);
        }
    }

    // If the `$HAB_CACHE_KEY_PATH` environment variable is not present, check if we are running
    // under a `sudo` invocation. If so, determine the non-root user that issued the command in
    // order to set their key cache location in the environment variable. This is done so that the
    // `hab-studio` command will find the correct key cache or so that the correct directory will
    // be volume mounted when used with Docker.
    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        if let Some(sudo_user) = henv::sudo_user() {
            if let Some(home) = users::get_home_for_user(&sudo_user) {
                let cache_key_path = home.join(format!(".{}", CACHE_KEY_PATH));
                debug!("Setting cache_key_path for SUDO_USER={} to: {}",
                       &sudo_user,
                       cache_key_path.display());
                env::set_var(CACHE_KEY_PATH_ENV_VAR, cache_key_path);
                // Prevent any inner `hab` invocations from triggering similar logic: we will be
                // operating in the context `hab-studio` which is running with rootlike privileges.
                env::remove_var("SUDO_USER");
            }
        }
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::path::PathBuf;
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{am_i_root, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;

    const STUDIO_CMD: &'static str = "hab-studio";
    const STUDIO_CMD_ENVVAR: &'static str = "HAB_STUDIO_BINARY";
    const STUDIO_PACKAGE_IDENT: &'static str = "core/hab-studio";
    const SUDO_CMD: &'static str = "sudo";

    pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let command = match henv::var(STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let ident = try!(PackageIdent::from_str(STUDIO_PACKAGE_IDENT));
                try!(exec::command_from_pkg(ui,
                                            STUDIO_CMD,
                                            &ident,
                                            &default_cache_key_path(None),
                                            0))
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            try!(process::become_command(cmd, args));
        } else {
            return Err(Error::ExecCommandNotFound(command.to_string_lossy().into_owned()));
        }
        Ok(())
    }

    pub fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
        // If I have root permissions, early return, we are done.
        if am_i_root() {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> =
                    vec!["-p".into(), "[sudo hab-studio] password for %u: ".into(), "-E".into()];
                args.append(&mut env::args_os().collect());
                Ok(try!(process::become_command(sudo_prog, args)))
            }
            None => {
                try!(ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                     SUDO_CMD)));
                try!(ui.warn("Running Habitat Studio requires root or administrator privileges. \
                              Please retry this command as a super user or use a \
                              privilege-granting facility such as sudo."));
                try!(ui.br());
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::process::{Command, Stdio};

    use common::ui::UI;
    use hcore::crypto::default_cache_key_path;
    use hcore::env as henv;
    use hcore::fs::{CACHE_KEY_PATH, find_command};
    use hcore::os::process;

    use error::{Error, Result};
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS: &'static str = "HAB_DOCKER_OPTS";

    pub fn start(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let docker = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        let cmd = match find_command(&docker) {
            Some(cmd) => cmd,
            None => return Err(Error::ExecCommandNotFound(docker.to_string())),
        };

        let child = Command::new(&cmd)
            .arg("pull")
            .arg(&image_identifier())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("docker failed to start");

        let output = child.wait_with_output()
            .expect("failed to wait on child");

        if output.status.success() {
            debug!("Docker image is reachable. Proceeding with launching docker.");
        } else {
            debug!("Docker image is unreachable. Exit code = {:?}",
                   output.status);

            let err_output = String::from_utf8(output.stderr).unwrap();

            if err_output.contains("image") && err_output.contains("not found") {
                return Err(Error::DockerImageNotFound(image_identifier().to_string()));
            } else if err_output.contains("Cannot connect to the Docker daemon") {
                return Err(Error::DockerDaemonDown);
            } else {
                return Err(Error::DockerNetworkDown(image_identifier().to_string()));
            }
        }

        let mut cmd_args: Vec<OsString> = vec!["run".into(),
                                               "--rm".into(),
                                               "--tty".into(),
                                               "--interactive".into(),
                                               "--privileged".into()];

        // All the args already placed in `cmd_args` are things that we don't want to insert again.
        // Later args such as `--env` will overwrite any options (potentially) set mistakenly here.
        if let Ok(opts) = henv::var(DOCKER_OPTS) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            debug!("Docker opts originating from DOCKER_OPTS = {:?}", opts);
            cmd_args.extend_from_slice(opts.as_slice());
        }

        let env_vars = vec!["HAB_DEPOT_URL", "HAB_ORIGIN", "http_proxy", "https_proxy"];
        for var in env_vars {
            if let Ok(val) = henv::var(var) {
                debug!("Propagating environment variable into container: {}={}",
                       var,
                       val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var, val).into());
            }
        }

        cmd_args.push("--volume".into());
        cmd_args.push("/var/run/docker.sock:/var/run/docker.sock".into());
        cmd_args.push("--volume".into());
        cmd_args.push(format!("{}:/{}",
                              default_cache_key_path(None).to_string_lossy(),
                              CACHE_KEY_PATH)
            .into());
        cmd_args.push("--volume".into());
        cmd_args.push(format!("{}:/src", env::current_dir().unwrap().to_string_lossy()).into());

        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting proxy environment variable '{}' before calling `{}'",
                       var,
                       docker);
                env::remove_var(var);
            }
        }

        debug!("Docker arguments = {:?}", cmd_args);
        Ok(try!(process::become_command(cmd, cmd_args)))
    }

    pub fn rerun_with_sudo_if_needed(_ui: &mut UI) -> Result<()> {
        // No sudo calls necessary here--we are calling `docker` commands instead
        Ok(())
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }

    #[cfg(test)]
    mod tests {
        use super::{image_identifier, DOCKER_IMAGE};
        use VERSION;

        #[test]
        fn retrieve_image_identifier() {
            assert_eq!(image_identifier(), format!("{}:{}", DOCKER_IMAGE, VERSION));
        }
    }
}
<commit_msg>Do not pull the studio docker image if you already have it.<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::ffi::OsString;

use common::ui::UI;
use hcore::crypto::CACHE_KEY_PATH_ENV_VAR;
use hcore::env as henv;
use hcore::fs::CACHE_KEY_PATH;
use hcore::os::users;

use config;
use error::Result;

pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
    try!(inner::rerun_with_sudo_if_needed(ui));

    // If the `$HAB_ORIGIN` environment variable is not present, then see if a default is set in
    // the CLI config. If so, set it as the `$HAB_ORIGIN` environment variable for the `hab-studio`
    // or `docker` execv call.
    if henv::var("HAB_ORIGIN").is_err() {
        let config = try!(config::load_with_sudo_user());
        if let Some(default_origin) = config.origin {
            debug!("Setting default origin {} via CLI config", &default_origin);
            env::set_var("HAB_ORIGIN", default_origin);
        }
    }

    // If the `$HAB_CACHE_KEY_PATH` environment variable is not present, check if we are running
    // under a `sudo` invocation. If so, determine the non-root user that issued the command in
    // order to set their key cache location in the environment variable. This is done so that the
    // `hab-studio` command will find the correct key cache or so that the correct directory will
    // be volume mounted when used with Docker.
    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        if let Some(sudo_user) = henv::sudo_user() {
            if let Some(home) = users::get_home_for_user(&sudo_user) {
                let cache_key_path = home.join(format!(".{}", CACHE_KEY_PATH));
                debug!("Setting cache_key_path for SUDO_USER={} to: {}",
                       &sudo_user,
                       cache_key_path.display());
                env::set_var(CACHE_KEY_PATH_ENV_VAR, cache_key_path);
                // Prevent any inner `hab` invocations from triggering similar logic: we will be
                // operating in the context `hab-studio` which is running with rootlike privileges.
                env::remove_var("SUDO_USER");
            }
        }
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::path::PathBuf;
    use std::str::FromStr;

    use common::ui::UI;
    use hcore::crypto::{init, default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{am_i_root, find_command};
    use hcore::os::process;
    use hcore::package::PackageIdent;

    use error::{Error, Result};
    use exec;

    const STUDIO_CMD: &'static str = "hab-studio";
    const STUDIO_CMD_ENVVAR: &'static str = "HAB_STUDIO_BINARY";
    const STUDIO_PACKAGE_IDENT: &'static str = "core/hab-studio";
    const SUDO_CMD: &'static str = "sudo";

    pub fn start(ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let command = match henv::var(STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let ident = try!(PackageIdent::from_str(STUDIO_PACKAGE_IDENT));
                try!(exec::command_from_pkg(ui,
                                            STUDIO_CMD,
                                            &ident,
                                            &default_cache_key_path(None),
                                            0))
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            try!(process::become_command(cmd, args));
        } else {
            return Err(Error::ExecCommandNotFound(command.to_string_lossy().into_owned()));
        }
        Ok(())
    }

    pub fn rerun_with_sudo_if_needed(ui: &mut UI) -> Result<()> {
        // If I have root permissions, early return, we are done.
        if am_i_root() {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> =
                    vec!["-p".into(), "[sudo hab-studio] password for %u: ".into(), "-E".into()];
                args.append(&mut env::args_os().collect());
                Ok(try!(process::become_command(sudo_prog, args)))
            }
            None => {
                try!(ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                     SUDO_CMD)));
                try!(ui.warn("Running Habitat Studio requires root or administrator privileges. \
                              Please retry this command as a super user or use a \
                              privilege-granting facility such as sudo."));
                try!(ui.br());
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use std::env;
    use std::ffi::OsString;
    use std::process::{Command, Stdio};

    use common::ui::UI;
    use hcore::crypto::default_cache_key_path;
    use hcore::env as henv;
    use hcore::fs::{CACHE_KEY_PATH, find_command};
    use hcore::os::process;

    use error::{Error, Result};
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS: &'static str = "HAB_DOCKER_OPTS";

    pub fn start(_ui: &mut UI, args: Vec<OsString>) -> Result<()> {
        let docker = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        let cmd = match find_command(&docker) {
            Some(cmd) => cmd,
            None => return Err(Error::ExecCommandNotFound(docker.to_string())),
        };

        let child = Command::new(&cmd)
            .arg("images")
            .arg(&image_identifier())
            .arg("-q")
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("docker failed to start");

        let output = child.wait_with_output().expect("failed to wait on child");
        let stdout = String::from_utf8(output.stdout).unwrap();
        if stdout.is_empty() {
            debug!("Failed to studio image locally.");

            let child = Command::new(&cmd)
                .arg("pull")
                .arg(&image_identifier())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .spawn()
                .expect("docker failed to start");

            let output = child.wait_with_output()
                .expect("failed to wait on child");

            if output.status.success() {
                debug!("Docker image is reachable. Proceeding with launching docker.");
            } else {
                debug!("Docker image is unreachable. Exit code = {:?}",
                       output.status);

                let err_output = String::from_utf8(output.stderr).unwrap();

                if err_output.contains("image") && err_output.contains("not found") {
                    return Err(Error::DockerImageNotFound(image_identifier().to_string()));
                } else if err_output.contains("Cannot connect to the Docker daemon") {
                    return Err(Error::DockerDaemonDown);
                } else {
                    return Err(Error::DockerNetworkDown(image_identifier().to_string()));
                }
            }
        } else {
            debug!("Found studio image locally.");
        }

        let mut cmd_args: Vec<OsString> = vec!["run".into(),
                                               "--rm".into(),
                                               "--tty".into(),
                                               "--interactive".into(),
                                               "--privileged".into()];

        // All the args already placed in `cmd_args` are things that we don't want to insert again.
        // Later args such as `--env` will overwrite any options (potentially) set mistakenly here.
        if let Ok(opts) = henv::var(DOCKER_OPTS) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            debug!("Docker opts originating from DOCKER_OPTS = {:?}", opts);
            cmd_args.extend_from_slice(opts.as_slice());
        }

        let env_vars = vec!["HAB_DEPOT_URL", "HAB_ORIGIN", "http_proxy", "https_proxy"];
        for var in env_vars {
            if let Ok(val) = henv::var(var) {
                debug!("Propagating environment variable into container: {}={}",
                       var,
                       val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var, val).into());
            }
        }

        cmd_args.push("--volume".into());
        cmd_args.push("/var/run/docker.sock:/var/run/docker.sock".into());
        cmd_args.push("--volume".into());
        cmd_args.push(format!("{}:/{}",
                              default_cache_key_path(None).to_string_lossy(),
                              CACHE_KEY_PATH)
            .into());
        cmd_args.push("--volume".into());
        cmd_args.push(format!("{}:/src", env::current_dir().unwrap().to_string_lossy()).into());

        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting proxy environment variable '{}' before calling `{}'",
                       var,
                       docker);
                env::remove_var(var);
            }
        }

        debug!("Docker arguments = {:?}", cmd_args);
        Ok(try!(process::become_command(cmd, cmd_args)))
    }

    pub fn rerun_with_sudo_if_needed(_ui: &mut UI) -> Result<()> {
        // No sudo calls necessary here--we are calling `docker` commands instead
        Ok(())
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }

    #[cfg(test)]
    mod tests {
        use super::{image_identifier, DOCKER_IMAGE};
        use VERSION;

        #[test]
        fn retrieve_image_identifier() {
            assert_eq!(image_identifier(), format!("{}:{}", DOCKER_IMAGE, VERSION));
        }
    }
}
<|endoftext|>


<commit_before>//! A reverse proxy application.
//!
//! This module provides a reverse proxy implementation for the Open
//! Matchmaking project.
//!
//! The purposes of the application  are handling client connections, applying
//! a middleware with checks for a token when it was specified and communicating
//! with a message broker for getting responses from microservices in the certain
//! format.
//!

use std::collections::{HashMap};
use std::error::{Error};
use std::net::{SocketAddr};
use std::sync::{Arc, Mutex, RwLock};

use amq_protocol::uri::{AMQPUri};
use cli::{CliOptions};
use futures::sync::{mpsc};
use futures::{Future, Sink};
use futures::stream::{Stream};
use tokio::net::{TcpListener};
use tokio::runtime::{run};
use tokio_current_thread::{spawn};
use tokio_tungstenite::{accept_async};
use tungstenite::protocol::{Message};

use auth::middleware::{Middleware, EmptyMiddleware};
use auth::token::middleware::{JwtTokenMiddleware};
use engine::{Engine};
use error::{PathfinderError};
use rabbitmq::client::{RabbitMQClient};
use rabbitmq::utils::{get_uri};

/// A reverse proxy application.
pub struct Proxy {
    engine: Arc<RwLock<Box<Engine>>>,
    amqp_uri: Arc<AMQPUri>,
    connections: Arc<Mutex<HashMap<SocketAddr, mpsc::UnboundedSender<Message>>>>,
    auth_middleware: Arc<RwLock<Box<Middleware>>>,
}

impl Proxy {
    /// Returns a new instance of a reverse proxy application.
    pub fn new(cli: &CliOptions) -> Proxy {
        let engine = Box::new(Engine::new(cli));
        let amqp_uri = get_uri(cli);
        let auth_middleware: Box<Middleware> = match cli.validate {
            true => Box::new(JwtTokenMiddleware::new(cli)),
            _ => Box::new(EmptyMiddleware::new(cli))
        };

        Proxy {
            engine: Arc::new(RwLock::new(engine)),
            amqp_uri: Arc::new(amqp_uri),
            connections: Arc::new(Mutex::new(HashMap::new())),
            auth_middleware: Arc::new(RwLock::new(auth_middleware)),
        }
    }

    /// Run the server on the specified address and port.
    pub fn run(&self, address: SocketAddr) {
        let listener = TcpListener::bind(&address).unwrap();
        info!("Listening on: {}", address);

        let engine = self.engine.clone();
        let connections = self.connections.clone();
        let auth_middleware = self.auth_middleware.clone();

        let server = |rabbitmq: Arc<RabbitMQClient>| {
            listener.incoming().for_each(move |stream| {
                let addr = stream.peer_addr().expect("Connected stream should have a peer address.");

                let engine_local = engine.clone();
                let rabbimq_local = rabbitmq.clone();
                let connections_local = connections.clone();
                let auth_middleware_local = auth_middleware.clone();

                accept_async(stream)
                    // Process the messages
                    .and_then(move |ws_stream| {
                        let connection_for_insert = connections_local.clone();
                        let connection_for_remove = connections_local.clone();
                        let connections_inner = connections_local.clone();

                        // Create a channel for the stream, which other sockets will use to
                        // send us messages. It could be used for broadcasting your data to
                        // another users in the future.
                        let (tx, rx) = mpsc::unbounded();
                        connection_for_insert.lock().unwrap().insert(addr, tx);

                        // Split the WebSocket stream so that it will be possible to work
                        // with the reading and writing halves separately.
                        let (sink, stream) = ws_stream.split();

                        // Read and process each message
                        let ws_reader = stream.for_each(move |message: Message| {

                            // Get references to required components
                            let addr_nested = addr.clone();
                            let engine_nested = engine_local.clone();
                            let connections_nested = connections_inner.clone();
                            let connections_for_transmitter = connections_inner.clone();
                            let transmitter_nested = &connections_for_transmitter.lock().unwrap()[&addr_nested];
                            let transmitter_nested2 = transmitter_nested.clone();

                            // 1. Deserialize message into JSON
                            let json_message = match engine_local.read().unwrap().deserialize_message(&message) {
                                Ok(json_message) => json_message,
                                Err(error) => {
                                    let formatted_error = format!("{}", error);
                                    let error_message = engine_nested.read().unwrap().wrap_an_error(formatted_error.as_str());
                                    transmitter_nested.unbounded_send(error_message).unwrap();
                                    return Ok(())
                                }
                            };

                            // 2. Apply a middleware to each incoming message
                            let auth_future = auth_middleware_local.read().unwrap()
                                .process_request(json_message.clone());

                            // 3. Put request into a queue in RabbitMQ and receive the response
                            let rabbitmq_future = engine_local.read().unwrap().handle(
                                json_message.clone(), transmitter_nested.clone(), rabbimq_local.clone()
                            );

                            let processing_request_future = auth_future
                                .and_then(move |_| rabbitmq_future)
                                .map_err(move |error| {
                                    let formatted_error = format!("{}", error);
                                    let error_message = engine_nested.read().unwrap().wrap_an_error(formatted_error.as_str());
                                    transmitter_nested2.unbounded_send(error_message).unwrap();
                                    ()
                                });

                            spawn(processing_request_future);
                            Ok(())
                        });

                        // Write back prepared responses
                        let ws_writer = rx.fold(sink, |mut sink, msg| {
                            sink.start_send(msg).unwrap();
                            Ok(sink)
                        });

                        // Wait for either half to be done to tear down the other
                        let connection = ws_reader.map(|_| ()).map_err(|_| ())
                            .select(ws_writer.map(|_| ()).map_err(|_| ()));

                        // Close the connection after using
                        spawn(connection.then(move |_| {
                            connection_for_remove.lock().unwrap().remove(&addr);
                            debug!("Connection {} closed.", addr);
                            Ok(())
                        }));

                        Ok(())
                    })
                    // An error occurred during the WebSocket handshake
                    .or_else(|error| {
                        debug!("{}", error.description());
                        Ok(())
                    })
            })
        };

        // Run the server
        let server_future = self.get_rabbitmq_client()
            .map_err(|error| {
                error!("Lapin error: {:?}", error);
                ()
            })
            .and_then(|rabbitmq: Arc<RabbitMQClient>| {
                server(rabbitmq)
                    .map_err(|_error| ())
            });
        run(server_future);
    }

    fn get_rabbitmq_client(&self)
        -> impl Future<Item=Arc<RabbitMQClient>, Error=PathfinderError> + Sync + Send + 'static
    {
        let amqp_uri = self.amqp_uri.clone();
        RabbitMQClient::connect(amqp_uri.as_ref())
            .map(|client| Arc::new(client))
            .map_err(|error| {
                error!("Error in RabbitMQ Client. Reason: {:?}", error);
                PathfinderError::Io(error)
            })
    }
}
<commit_msg>Removed excessive synchronization instances<commit_after>//! A reverse proxy application.
//!
//! This module provides a reverse proxy implementation for the Open
//! Matchmaking project.
//!
//! The purposes of the application  are handling client connections, applying
//! a middleware with checks for a token when it was specified and communicating
//! with a message broker for getting responses from microservices in the certain
//! format.
//!

use std::collections::{HashMap};
use std::error::{Error};
use std::net::{SocketAddr};
use std::sync::{Arc, Mutex};

use amq_protocol::uri::{AMQPUri};
use cli::{CliOptions};
use futures::sync::{mpsc};
use futures::{Future, Sink};
use futures::stream::{Stream};
use tokio::net::{TcpListener};
use tokio::runtime::{run};
use tokio_current_thread::{spawn};
use tokio_tungstenite::{accept_async};
use tungstenite::protocol::{Message};

use auth::middleware::{Middleware, EmptyMiddleware};
use auth::token::middleware::{JwtTokenMiddleware};
use engine::{Engine};
use error::{PathfinderError};
use rabbitmq::client::{RabbitMQClient};
use rabbitmq::utils::{get_uri};

/// A reverse proxy application.
pub struct Proxy {
    engine: Arc<Engine>,
    amqp_uri: Arc<AMQPUri>,
    connections: Arc<Mutex<HashMap<SocketAddr, mpsc::UnboundedSender<Message>>>>,
    auth_middleware: Arc<Box<Middleware>>,
}

impl Proxy {
    /// Returns a new instance of a reverse proxy application.
    pub fn new(cli: &CliOptions) -> Proxy {
        let engine = Engine::new(cli);
        let amqp_uri = get_uri(cli);
        let auth_middleware: Box<Middleware> = match cli.validate {
            true => Box::new(JwtTokenMiddleware::new(cli)),
               _ => Box::new(EmptyMiddleware::new(cli))
        };

        Proxy {
            engine: Arc::new(engine),
            amqp_uri: Arc::new(amqp_uri),
            connections: Arc::new(Mutex::new(HashMap::new())),
            auth_middleware: Arc::new(auth_middleware),
        }
    }

    /// Run the server on the specified address and port.
    pub fn run(&self, address: SocketAddr) {
        let listener = TcpListener::bind(&address).unwrap();
        info!("Listening on: {}", address);

        let engine = self.engine.clone();
        let connections = self.connections.clone();
        let auth_middleware = self.auth_middleware.clone();

        let server = |rabbitmq: Arc<RabbitMQClient>| {
            listener.incoming().for_each(move |stream| {
                let addr = stream.peer_addr().expect("Connected stream should have a peer address.");

                let engine_local = engine.clone();
                let rabbimq_local = rabbitmq.clone();
                let connections_local = connections.clone();
                let auth_middleware_local = auth_middleware.clone();

                accept_async(stream)
                    // Process the messages
                    .and_then(move |ws_stream| {
                        let connection_for_insert = connections_local.clone();
                        let connection_for_remove = connections_local.clone();
                        let connections_inner = connections_local.clone();

                        // Create a channel for the stream, which other sockets will use to
                        // send us messages. It could be used for broadcasting your data to
                        // another users in the future.
                        let (tx, rx) = mpsc::unbounded();
                        connection_for_insert.lock().unwrap().insert(addr, tx);

                        // Split the WebSocket stream so that it will be possible to work
                        // with the reading and writing halves separately.
                        let (sink, stream) = ws_stream.split();

                        // Read and process each message
                        let ws_reader = stream.for_each(move |message: Message| {

                            // Get references to required components
                            let addr_nested = addr.clone();
                            let engine_nested = engine_local.clone();
                            let connections_nested = connections_inner.clone();
                            let connections_for_transmitter = connections_inner.clone();
                            let transmitter_nested = &connections_for_transmitter.lock().unwrap()[&addr_nested];
                            let transmitter_nested2 = transmitter_nested.clone();

                            // 1. Deserialize message into JSON
                            let json_message = match engine_local.deserialize_message(&message) {
                                Ok(json_message) => json_message,
                                Err(error) => {
                                    let formatted_error = format!("{}", error);
                                    let error_message = engine_nested.wrap_an_error(formatted_error.as_str());
                                    transmitter_nested.unbounded_send(error_message).unwrap();
                                    return Ok(())
                                }
                            };

                            // 2. Apply a middleware to each incoming message
                            let auth_future = auth_middleware_local.process_request(json_message.clone());

                            // 3. Put request into a queue in RabbitMQ and receive the response
                            let rabbitmq_future = engine_local.handle(
                                json_message.clone(), transmitter_nested.clone(), rabbimq_local.clone()
                            );

                            let processing_request_future = auth_future
                                .and_then(move |_| rabbitmq_future)
                                .map_err(move |error| {
                                    let formatted_error = format!("{}", error);
                                    let error_message = engine_nested.wrap_an_error(formatted_error.as_str());
                                    transmitter_nested2.unbounded_send(error_message).unwrap();
                                    ()
                                });

                            spawn(processing_request_future);
                            Ok(())
                        });

                        // Write back prepared responses
                        let ws_writer = rx.fold(sink, |mut sink, msg| {
                            sink.start_send(msg).unwrap();
                            Ok(sink)
                        });

                        // Wait for either half to be done to tear down the other
                        let connection = ws_reader.map(|_| ()).map_err(|_| ())
                            .select(ws_writer.map(|_| ()).map_err(|_| ()));

                        // Close the connection after using
                        spawn(connection.then(move |_| {
                            connection_for_remove.lock().unwrap().remove(&addr);
                            debug!("Connection {} closed.", addr);
                            Ok(())
                        }));

                        Ok(())
                    })
                    // An error occurred during the WebSocket handshake
                    .or_else(|error| {
                        debug!("{}", error.description());
                        Ok(())
                    })
            })
        };

        // Run the server
        let server_future = self.get_rabbitmq_client()
            .map_err(|error| {
                error!("Lapin error: {:?}", error);
                ()
            })
            .and_then(|rabbitmq: Arc<RabbitMQClient>| {
                server(rabbitmq)
                    .map_err(|_error| ())
            });
        run(server_future);
    }

    fn get_rabbitmq_client(&self)
        -> impl Future<Item=Arc<RabbitMQClient>, Error=PathfinderError> + Sync + Send + 'static
    {
        let amqp_uri = self.amqp_uri.clone();
        RabbitMQClient::connect(amqp_uri.as_ref())
            .map(|client| Arc::new(client))
            .map_err(|error| {
                error!("Error in RabbitMQ Client. Reason: {:?}", error);
                PathfinderError::Io(error)
            })
    }
}
<|endoftext|>