<commit_before>//! [GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey)

use ruma_api::ruma_api;
use ruma_events::EventType;
use ruma_identifiers::RoomId;
use ruma_serde::Outgoing;
use serde_json::value::RawValue as RawJsonValue;

ruma_api! {
    metadata: {
        description: "Get state events associated with a given key.",
        method: GET,
        name: "get_state_events_for_key",
        path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key",
        rate_limited: false,
        authentication: AccessToken,
    }

    response: {
        /// The content of the state event.
        #[ruma_api(body)]
        pub content: Box<RawJsonValue>,
    }

    error: crate::Error
}

/// Data for a request to the `get_state_events_for_key` API endpoint.
///
/// Get state events associated with a given key.
#[derive(Clone, Debug, Outgoing)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(!Deserialize)]
pub struct Request<'a> {
    /// The room to look up the state for.
    pub room_id: &'a RoomId,

    /// The type of state to look up.
    pub event_type: EventType,

    /// The key of the state to look up.
    pub state_key: &'a str,
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given room ID, event type and state key.
    pub fn new(room_id: &'a RoomId, event_type: EventType, state_key: &'a str) -> Self {
        Self { room_id, event_type, state_key }
    }
}

impl Response {
    /// Creates a new `Response` with the given content.
    pub fn new(content: Box<RawJsonValue>) -> Self {
        Self { content }
    }
}

#[cfg(feature = "client")]
impl<'a> ruma_api::OutgoingRequest for Request<'a> {
    type EndpointError = crate::Error;
    type IncomingResponse = <Response as ruma_serde::Outgoing>::Incoming;

    const METADATA: ruma_api::Metadata = METADATA;

    fn try_into_http_request(
        self,
        base_url: &str,
        access_token: Option<&str>,
    ) -> Result<http::Request<Vec<u8>>, ruma_api::error::IntoHttpError> {
        use std::borrow::Cow;

        use http::header::{HeaderValue, AUTHORIZATION, CONTENT_TYPE};
        use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};

        let mut url = format!(
            "{}/_matrix/client/r0/rooms/{}/state/{}",
            base_url.strip_suffix('/').unwrap_or(base_url),
            utf8_percent_encode(&self.room_id.to_string(), NON_ALPHANUMERIC,),
            utf8_percent_encode(&self.event_type.to_string(), NON_ALPHANUMERIC,)
        );

        if !self.state_key.is_empty() {
            url.push('/');
            url.push_str(&Cow::from(utf8_percent_encode(&self.state_key, NON_ALPHANUMERIC)));
        }

        http::Request::builder()
            .method(http::Method::GET)
            .uri(url)
            .header(CONTENT_TYPE, "application/json")
            .header(
                AUTHORIZATION,
                HeaderValue::from_str(&format!(
                    "Bearer {}",
                    access_token.ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)?
                ))?,
            )
            .body(Vec::new())
            .map_err(Into::into)
    }
}

#[cfg(feature = "server")]
impl ruma_api::IncomingRequest for IncomingRequest {
    type EndpointError = crate::Error;
    type OutgoingResponse = Response;

    const METADATA: ruma_api::Metadata = METADATA;

    fn try_from_http_request<T: AsRef<[u8]>>(
        request: http::Request<T>,
    ) -> Result<Self, ruma_api::error::FromHttpRequestError> {
        use std::convert::TryFrom;

        let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();

        let room_id = {
            let decoded =
                percent_encoding::percent_decode(path_segments[4].as_bytes()).decode_utf8()?;

            RoomId::try_from(&*decoded)?
        };

        let event_type = {
            let decoded =
                percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8()?;

            EventType::try_from(&*decoded)?
        };

        let state_key = match path_segments.get(7) {
            Some(segment) => {
                let decoded = percent_encoding::percent_decode(segment.as_bytes()).decode_utf8()?;

                String::try_from(&*decoded)?
            }
            None => "".into(),
        };

        Ok(Self { room_id, event_type, state_key })
    }
}
<commit_msg>client-api: Use ruma_serde::Raw instead of RawJsonValue in get_state_events_for_key<commit_after>//! [GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey)

use ruma_api::ruma_api;
use ruma_events::{AnyStateEventContent, EventType};
use ruma_identifiers::RoomId;
use ruma_serde::{Outgoing, Raw};

ruma_api! {
    metadata: {
        description: "Get state events associated with a given key.",
        method: GET,
        name: "get_state_events_for_key",
        path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key",
        rate_limited: false,
        authentication: AccessToken,
    }

    response: {
        /// The content of the state event.
        ///
        /// Since the inner type of the `Raw` does not implement `Deserialize`, you need to use
        /// `ruma_events::RawExt` to deserialize it.
        #[ruma_api(body)]
        pub content: Raw<AnyStateEventContent>,
    }

    error: crate::Error
}

/// Data for a request to the `get_state_events_for_key` API endpoint.
///
/// Get state events associated with a given key.
#[derive(Clone, Debug, Outgoing)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(!Deserialize)]
pub struct Request<'a> {
    /// The room to look up the state for.
    pub room_id: &'a RoomId,

    /// The type of state to look up.
    pub event_type: EventType,

    /// The key of the state to look up.
    pub state_key: &'a str,
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given room ID, event type and state key.
    pub fn new(room_id: &'a RoomId, event_type: EventType, state_key: &'a str) -> Self {
        Self { room_id, event_type, state_key }
    }
}

impl Response {
    /// Creates a new `Response` with the given content.
    pub fn new(content: Raw<AnyStateEventContent>) -> Self {
        Self { content }
    }
}

#[cfg(feature = "client")]
impl<'a> ruma_api::OutgoingRequest for Request<'a> {
    type EndpointError = crate::Error;
    type IncomingResponse = <Response as ruma_serde::Outgoing>::Incoming;

    const METADATA: ruma_api::Metadata = METADATA;

    fn try_into_http_request(
        self,
        base_url: &str,
        access_token: Option<&str>,
    ) -> Result<http::Request<Vec<u8>>, ruma_api::error::IntoHttpError> {
        use std::borrow::Cow;

        use http::header::{HeaderValue, AUTHORIZATION, CONTENT_TYPE};
        use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};

        let mut url = format!(
            "{}/_matrix/client/r0/rooms/{}/state/{}",
            base_url.strip_suffix('/').unwrap_or(base_url),
            utf8_percent_encode(&self.room_id.to_string(), NON_ALPHANUMERIC),
            utf8_percent_encode(&self.event_type.to_string(), NON_ALPHANUMERIC)
        );

        if !self.state_key.is_empty() {
            url.push('/');
            url.push_str(&Cow::from(utf8_percent_encode(&self.state_key, NON_ALPHANUMERIC)));
        }

        http::Request::builder()
            .method(http::Method::GET)
            .uri(url)
            .header(CONTENT_TYPE, "application/json")
            .header(
                AUTHORIZATION,
                HeaderValue::from_str(&format!(
                    "Bearer {}",
                    access_token.ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)?
                ))?,
            )
            .body(Vec::new())
            .map_err(Into::into)
    }
}

#[cfg(feature = "server")]
impl ruma_api::IncomingRequest for IncomingRequest {
    type EndpointError = crate::Error;
    type OutgoingResponse = Response;

    const METADATA: ruma_api::Metadata = METADATA;

    fn try_from_http_request<T: AsRef<[u8]>>(
        request: http::Request<T>,
    ) -> Result<Self, ruma_api::error::FromHttpRequestError> {
        use std::convert::TryFrom;

        let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();

        let room_id = {
            let decoded =
                percent_encoding::percent_decode(path_segments[4].as_bytes()).decode_utf8()?;

            RoomId::try_from(&*decoded)?
        };

        let event_type = {
            let decoded =
                percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8()?;

            EventType::try_from(&*decoded)?
        };

        let state_key = match path_segments.get(7) {
            Some(segment) => {
                let decoded = percent_encoding::percent_decode(segment.as_bytes()).decode_utf8()?;

                String::try_from(&*decoded)?
            }
            None => "".into(),
        };

        Ok(Self { room_id, event_type, state_key })
    }
}
<|endoftext|>


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

extern crate mozangle;
extern crate webrender;

use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};

// from glslang
const FRAGMENT_SHADER: u32 = 0x8B30;
const VERTEX_SHADER: u32 = 0x8B31;

struct Shader {
    name: &'static str,
    features: &'static [&'static str],
}

const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024\n";

const CLIP_FEATURES: &[&str] = &["TRANSFORM"];
const CACHE_FEATURES: &[&str] = &[""];
const PRIM_FEATURES: &[&str] = &["", "TRANSFORM"];

const SHADERS: &[Shader] = &[
    // Clip mask shaders
    Shader {
        name: "cs_clip_rectangle",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_image",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_box_shadow",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_line",
        features: CLIP_FEATURES,
    },
    // Cache shaders
    Shader {
        name: "cs_blur",
        features: CACHE_FEATURES,
    },
    Shader {
        name: "cs_border_segment",
        features: CACHE_FEATURES,
    },
    // Prim shaders
    Shader {
        name: "ps_split_composite",
        features: PRIM_FEATURES,
    },
    Shader {
        name: "ps_text_run",
        features: PRIM_FEATURES,
    },
    // Brush shaders
    Shader {
        name: "brush_yuv_image",
        features: &["", "YUV_NV12", "YUV_PLANAR", "YUV_INTERLEAVED", "YUV_NV12,TEXTURE_RECT"],
    },
    Shader {
        name: "brush_solid",
        features: &[],
    },
    Shader {
        name: "brush_image",
        features: &["", "ALPHA_PASS"],
    },
    Shader {
        name: "brush_blend",
        features: &[],
    },
    Shader {
        name: "brush_mix_blend",
        features: &[],
    },
    Shader {
        name: "brush_radial_gradient",
        features: &[ "DITHERING" ],
    },
    Shader {
        name: "brush_linear_gradient",
        features: &[],
    },
];

const VERSION_STRING: &str = "#version 300 es\n";

#[test]
fn validate_shaders() {
    mozangle::shaders::initialize().unwrap();

    let resources = BuiltInResources::default();
    let vs_validator =
        ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    let fs_validator =
        ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    for shader in SHADERS {
        for config in shader.features {
            let mut features = String::new();
            features.push_str(SHADER_PREFIX);

            for feature in config.split(",") {
                features.push_str(&format!("#define WR_FEATURE_{}", feature));
            }

            let (vs, fs) =
                webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None);

            validate(&vs_validator, shader.name, vs);
            validate(&fs_validator, shader.name, fs);
        }
    }
}

fn validate(validator: &ShaderValidator, name: &str, source: String) {
    // Check for each `switch` to have a `default`, see
    // https://github.com/servo/webrender/wiki/Driver-issues#lack-of-default-case-in-a-switch
    assert_eq!(source.matches("switch").count(), source.matches("default:").count(),
        "Shader '{}' doesn't have all `switch` covered with `default` cases", name);
    // Run Angle validator
    match validator.compile_and_translate(&[&source]) {
        Ok(_) => {
            println!("Shader translated succesfully: {}", name);
        }
        Err(_) => {
            panic!(
                "Shader compilation failed: {}\n{}",
                name,
                validator.info_log()
            );
        }
    }
}
<commit_msg>Add a missing new line to shader feature strings in angle shader validation, and update shader features<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate mozangle;
extern crate webrender;

use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};

// from glslang
const FRAGMENT_SHADER: u32 = 0x8B30;
const VERTEX_SHADER: u32 = 0x8B31;

struct Shader {
    name: &'static str,
    features: &'static [&'static str],
}

const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024\n";

const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"];
const CLIP_FEATURES: &[&str] = &["TRANSFORM"];
const CACHE_FEATURES: &[&str] = &[""];
const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ];
const PRIM_FEATURES: &[&str] = &[""];

const SHADERS: &[Shader] = &[
    // Clip mask shaders
    Shader {
        name: "cs_clip_rectangle",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_image",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_box_shadow",
        features: CLIP_FEATURES,
    },
    Shader {
        name: "cs_clip_line",
        features: CLIP_FEATURES,
    },
    // Cache shaders
    Shader {
        name: "cs_blur",
        features: &[ "ALPHA_TARGET", "COLOR_TARGET" ],
    },
    Shader {
        name: "cs_border_segment",
        features: CACHE_FEATURES,
    },
    // Prim shaders
    Shader {
        name: "ps_split_composite",
        features: PRIM_FEATURES,
    },
    Shader {
        name: "ps_text_run",
        features: &[ "", "GLYPH_TRANSFORM" ],
    },
    // Brush shaders
    Shader {
        name: "brush_yuv_image",
        features: &[
            "",
            "YUV_NV12",
            "YUV_PLANAR",
            "YUV_INTERLEAVED",
            "TEXTURE_2D,YUV_NV12",
            "YUV_NV12,ALPHA_PASS",
        ],
    },
    Shader {
        name: "brush_solid",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_image",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_mix_blend",
        features: BRUSH_FEATURES,
    },
    Shader {
        name: "brush_radial_gradient",
        features: GRADIENT_FEATURES,
    },
    Shader {
        name: "brush_linear_gradient",
        features: GRADIENT_FEATURES,
    },
];

const VERSION_STRING: &str = "#version 300 es\n";

#[test]
fn validate_shaders() {
    mozangle::shaders::initialize().unwrap();

    let resources = BuiltInResources::default();
    let vs_validator =
        ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    let fs_validator =
        ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();

    for shader in SHADERS {
        for config in shader.features {
            let mut features = String::new();
            features.push_str(SHADER_PREFIX);

            for feature in config.split(",") {
                features.push_str(&format!("#define WR_FEATURE_{}\n", feature));
            }

            let (vs, fs) =
                webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None);

            validate(&vs_validator, shader.name, vs);
            validate(&fs_validator, shader.name, fs);
        }
    }
}

fn validate(validator: &ShaderValidator, name: &str, source: String) {
    // Check for each `switch` to have a `default`, see
    // https://github.com/servo/webrender/wiki/Driver-issues#lack-of-default-case-in-a-switch
    assert_eq!(source.matches("switch").count(), source.matches("default:").count(),
        "Shader '{}' doesn't have all `switch` covered with `default` cases", name);
    // Run Angle validator
    match validator.compile_and_translate(&[&source]) {
        Ok(_) => {
            println!("Shader translated succesfully: {}", name);
        }
        Err(_) => {
            panic!(
                "Shader compilation failed: {}\n{}",
                name,
                validator.info_log()
            );
        }
    }
}
<|endoftext|>


<commit_before>//! Safe wrappers around Erlang binaries.
//!
//! Rustler provides two binary types: [`Binary`] and [`OwnedBinary`].  Both
//! represent a contiguous region `u8`s, and they both use the Erlang allocator. The
//! primary difference between the two is their ownership semantics.
//!
//! The _owned_ in `OwnedBinary` refers to the fact that it owns the binary it
//! wraps. The _owner_ of an `OwnedBinary` is free to modify its contents. Ownership
//! lasts until it is dropped or consumed by converting it into a regular
//! `Binary`. An `OwnedBinary` can not be copied or cloned and is thus always moved.
//!
//! The `Binary` type is an immutable shared-reference to a binary. `Binary`s are
//! cheap to copy: all copies of a `Binary` point to the original `Binary`'s
//! data. Additionally, a `Binary`'s lifetime is tied to that of the NIF's [`Env`],
//! preventing outstanding references to the data after a NIF returns.
//!
//! # Examples
//!
//! Constructing an `OwnedBinary`:
//!
//! ```no_run
//! # use rustler::OwnedBinary;
//! {
//!     let mut bin = OwnedBinary::new(5).expect("allocation failed");
//!     bin.as_mut_slice().copy_from_slice("hello".as_bytes());
//! } // <- `bin` is dropped here
//! ```
//!
//! The following NIF takes a binary as its only parameter and returns a new binary
//! where each element is exclusive-or'ed with a constant:
//!
//! ```no_run
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! #[rustler::nif]
//! fn xor_example<'a>(env: Env<'a>, bin: Binary<'a>) -> NifResult<Binary<'a>> {
//!     let mut owned: OwnedBinary = bin.to_owned().ok_or(Error::Term(Box::new("no mem")))?;
//!     for byte in owned.as_mut_slice() {
//!         *byte ^= 0xAA;
//!     }
//!
//!     // Ownership of `owned`'s data is transferred to `env` on the
//!     // following line, so no additional heap allocations are incurred.
//!     Ok(Binary::from_owned(owned, env))
//! }
//! ```
//!
//! The contents of a newly-allocated `OwnedBinary` is not initialized to any
//! particular value. If your usage of the binary requires the it's data to be
//! zeroed, for example, then you must explicit zero it. In this example, we
//! manually zeroize the binary before passing it as slice to a third party
//! function.
//!
//! ```no_run
//! # fn some_third_party_api(buf: &mut [u8]) {
//! #     for elem in buf {
//! #         if *elem == 0 { *elem = 1 } else { panic!("Not a zero!") }
//! #     }
//! # }
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! #[rustler::nif]
//! fn wrapper_for_some_<'a>(env: Env<'a>) -> NifResult<Binary<'a>> {
//!     let mut owned = OwnedBinary::new(100).ok_or(Error::Term(Box::new("no mem")))?;
//!     for byte in owned.as_mut_slice() {
//!         *byte = 0;
//!     }
//!
//!     // Some third party API which requires the slice to be all zeros on entry.
//!     some_third_party_api(owned.as_mut_slice());
//!
//!     // The imaginary API call presumedly filled in our binary with meaningful
//!     // data, so let's return it.
//!     Ok(Binary::from_owned(owned, env))
//! }
//!
//! ```
//!
//! [`Binary`]: struct.Binary.html
//! [`Env`]: ../../env/struct.Env.html
//! [`OwnedBinary`]: struct.OwnedBinary.html

use crate::{
    wrapper::binary::{alloc, realloc, ErlNifBinary},
    Decoder, Encoder, Env, Error, NifResult, Term,
};
use std::{
    borrow::{Borrow, BorrowMut},
    io::Write,
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
};

/// An mutable smart-pointer to an Erlang binary.
///
/// See [module-level doc](index.html) for more information.
pub struct OwnedBinary(ErlNifBinary);

impl<'a> OwnedBinary {
    pub unsafe fn from_raw(inner: ErlNifBinary) -> OwnedBinary {
        OwnedBinary(inner)
    }

    /// Allocates a new `OwnedBinary` with size `size`.
    ///
    /// Memory is not be initialized. If uninitialized memory is undesirable, set it
    /// manually.
    ///
    /// # Errors
    ///
    /// If allocation fails, `None` is returned.
    pub fn new(size: usize) -> Option<OwnedBinary> {
        unsafe { alloc(size) }.map(OwnedBinary)
    }

    /// Copies `src`'s data into a new `OwnedBinary`.
    ///
    /// # Errors
    ///
    /// If allocation fails, `None` is returned.
    pub fn from_unowned(src: &Binary) -> Option<OwnedBinary> {
        OwnedBinary::new(src.len()).map(|mut b| {
            b.as_mut_slice().copy_from_slice(&src);
            b
        })
    }

    /// Attempts to reallocate `self` with the new size.
    ///
    /// Memory outside the range of the original binary will not be initialized. If
    /// uninitialized memory is undesirable, set it manually.
    ///
    /// # Errors
    ///
    /// If reallocation fails, `false` is returned. Data remains intact on error.
    #[must_use]
    pub fn realloc(&mut self, size: usize) -> bool {
        unsafe { realloc(&mut self.0, size) }
    }

    /// Attempts to reallocate `self` with the new size.
    ///
    /// If reallocation fails, it will perform a copy instead.
    ///
    /// Memory outside the range of the original binary will not be initialized. If
    /// uninitialized memory is undesirable, set it manually.
    pub fn realloc_or_copy(&mut self, size: usize) {
        if !self.realloc(size) {
            let mut new = OwnedBinary::new(size).unwrap();
            if let Ok(num_written) = new.as_mut_slice().write(self.as_slice()) {
                if !(num_written == self.len() || num_written == new.len()) {
                    panic!("Could not copy binary");
                }
                ::std::mem::swap(&mut self.0, &mut new.0);
            } else {
                panic!("Could not copy binary");
            }
        }
    }

    /// Extracts a slice containing the entire binary.
    pub fn as_slice(&self) -> &[u8] {
        unsafe { ::std::slice::from_raw_parts(self.0.data, self.0.size) }
    }

    /// Extracts a mutable slice of the entire binary.
    pub fn as_mut_slice(&mut self) -> &mut [u8] {
        unsafe { ::std::slice::from_raw_parts_mut(self.0.data, self.0.size) }
    }

    /// Consumes `self` and returns an immutable `Binary`.
    ///
    /// This method is the mirror of [`Binary::from_owned`], and they can be used
    /// interchangeably.
    ///
    /// [`Binary::from_owned`]: struct.Binary.html#method.from_owned
    pub fn release(self, env: Env) -> Binary {
        Binary::from_owned(self, env)
    }
}

impl Borrow<[u8]> for OwnedBinary {
    fn borrow(&self) -> &[u8] {
        self.as_slice()
    }
}
impl BorrowMut<[u8]> for OwnedBinary {
    fn borrow_mut(&mut self) -> &mut [u8] {
        self.as_mut_slice()
    }
}
impl Deref for OwnedBinary {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        self.as_slice()
    }
}
impl DerefMut for OwnedBinary {
    fn deref_mut(&mut self) -> &mut [u8] {
        self.as_mut_slice()
    }
}

impl Drop for OwnedBinary {
    fn drop(&mut self) {
        unsafe { rustler_sys::enif_release_binary(&mut self.0) };
    }
}

unsafe impl Send for OwnedBinary {}

/// An immutable smart-pointer to an Erlang binary.
///
/// See [module-level doc](index.html) for more information.
#[derive(Copy, Clone)]
pub struct Binary<'a> {
    inner: ErlNifBinary,
    term: Term<'a>,
}

impl<'a> Binary<'a> {
    /// Consumes `owned` and returns an immutable `Binary`.
    pub fn from_owned(owned: OwnedBinary, env: Env<'a>) -> Self {
        // We are transferring ownership of `owned`'s data to the
        // environment. Therefore, we need to prevent `owned`'s destructor being
        // called at the end of this scope. The least error-prone solution (compared
        // to `mem::forget()`) is to wrap `owned` in a `ManuallyDrop` and EXPLICITLY
        // NOT CALL `ManuallyDrop::drop()`.
        let mut owned = std::mem::ManuallyDrop::new(owned);
        let term = unsafe {
            Term::new(
                env,
                rustler_sys::enif_make_binary(env.as_c_arg(), &mut owned.0),
            )
        };
        Binary {
            inner: owned.0,
            term,
        }
    }

    /// Copies `self`'s data into a new `OwnedBinary`.
    ///
    /// # Errors
    ///
    /// If allocation fails, an error will be returned.
    pub fn to_owned(&self) -> Option<OwnedBinary> {
        OwnedBinary::from_unowned(self)
    }

    /// Creates a `Binary` from `term`.
    ///
    /// # Errors
    ///
    /// If `term` is not a binary, an error will be returned.
    pub fn from_term(term: Term<'a>) -> Result<Self, Error> {
        let mut binary = MaybeUninit::uninit();
        if unsafe {
            rustler_sys::enif_inspect_binary(
                term.get_env().as_c_arg(),
                term.as_c_arg(),
                binary.as_mut_ptr(),
            )
        } == 0
        {
            return Err(Error::BadArg);
        }
        Ok(Binary {
            inner: unsafe { binary.assume_init() },
            term,
        })
    }

    /// Creates a `Binary` from `term`.
    ///
    /// # Errors
    ///
    /// If `term` is not an `iolist`, an error will be returned.
    pub fn from_iolist(term: Term<'a>) -> Result<Self, Error> {
        let mut binary = MaybeUninit::uninit();
        if unsafe {
            rustler_sys::enif_inspect_iolist_as_binary(
                term.get_env().as_c_arg(),
                term.as_c_arg(),
                binary.as_mut_ptr(),
            )
        } == 0
        {
            return Err(Error::BadArg);
        }
        Ok(Binary {
            inner: unsafe { binary.assume_init() },
            term,
        })
    }

    /// Returns an Erlang term representation of `self`.
    pub fn to_term<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.term.in_env(env)
    }

    /// Extracts a slice containing the entire binary.
    pub fn as_slice(&self) -> &'a [u8] {
        unsafe { ::std::slice::from_raw_parts(self.inner.data, self.inner.size) }
    }

    /// Returns a new view into the same binary.
    ///
    /// This method is analogous to subslicing (e.g. `some_data[offset..length]`) in
    /// that it does not copy nor allocate data.
    ///
    /// # Errors
    ///
    /// If `offset + length` is out of bounds, an error will be returned.
    pub fn make_subbinary(&self, offset: usize, length: usize) -> NifResult<Binary<'a>> {
        let min_len = length.checked_add(offset);
        if min_len.ok_or(Error::BadArg)? > self.inner.size {
            return Err(Error::BadArg);
        }

        let raw_term = unsafe {
            rustler_sys::enif_make_sub_binary(
                self.term.get_env().as_c_arg(),
                self.term.as_c_arg(),
                offset,
                length,
            )
        };
        let term = unsafe { Term::new(self.term.get_env(), raw_term) };
        // This should never fail, as we are always passing in a binary term.
        Ok(Binary::from_term(term).ok().unwrap())
    }
}

impl<'a> Borrow<[u8]> for Binary<'a> {
    fn borrow(&self) -> &[u8] {
        self.as_slice()
    }
}
impl<'a> Deref for Binary<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        self.as_slice()
    }
}

impl<'a> Decoder<'a> for Binary<'a> {
    fn decode(term: Term<'a>) -> Result<Self, Error> {
        Binary::from_term(term)
    }
}
impl<'a> Encoder for Binary<'a> {
    fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.to_term(env)
    }
}

/// ## Binary terms
impl<'a> Term<'a> {
    pub fn into_binary(self) -> NifResult<Binary<'a>> {
        Binary::from_term(self)
    }
}
<commit_msg>Update rustler/src/types/binary.rs<commit_after>//! Safe wrappers around Erlang binaries.
//!
//! Rustler provides two binary types: [`Binary`] and [`OwnedBinary`].  Both
//! represent a contiguous region `u8`s, and they both use the Erlang allocator. The
//! primary difference between the two is their ownership semantics.
//!
//! The _owned_ in `OwnedBinary` refers to the fact that it owns the binary it
//! wraps. The _owner_ of an `OwnedBinary` is free to modify its contents. Ownership
//! lasts until it is dropped or consumed by converting it into a regular
//! `Binary`. An `OwnedBinary` can not be copied or cloned and is thus always moved.
//!
//! The `Binary` type is an immutable shared-reference to a binary. `Binary`s are
//! cheap to copy: all copies of a `Binary` point to the original `Binary`'s
//! data. Additionally, a `Binary`'s lifetime is tied to that of the NIF's [`Env`],
//! preventing outstanding references to the data after a NIF returns.
//!
//! # Examples
//!
//! Constructing an `OwnedBinary`:
//!
//! ```no_run
//! # use rustler::OwnedBinary;
//! {
//!     let mut bin = OwnedBinary::new(5).expect("allocation failed");
//!     bin.as_mut_slice().copy_from_slice("hello".as_bytes());
//! } // <- `bin` is dropped here
//! ```
//!
//! The following NIF takes a binary as its only parameter and returns a new binary
//! where each element is exclusive-or'ed with a constant:
//!
//! ```no_run
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! #[rustler::nif]
//! fn xor_example<'a>(env: Env<'a>, bin: Binary<'a>) -> NifResult<Binary<'a>> {
//!     let mut owned: OwnedBinary = bin.to_owned().ok_or(Error::Term(Box::new("no mem")))?;
//!     for byte in owned.as_mut_slice() {
//!         *byte ^= 0xAA;
//!     }
//!
//!     // Ownership of `owned`'s data is transferred to `env` on the
//!     // following line, so no additional heap allocations are incurred.
//!     Ok(Binary::from_owned(owned, env))
//! }
//! ```
//!
//! The contents of a newly-allocated `OwnedBinary` is not initialized to any
//! particular value. If your usage of the binary requires the it's data to be
//! zeroed, for example, then you must explicit zero it. In this example, we
//! manually zeroize the binary before passing it as slice to a third party
//! function.
//!
//! ```no_run
//! # fn some_third_party_api(buf: &mut [u8]) {
//! #     for elem in buf {
//! #         if *elem == 0 { *elem = 1 } else { panic!("Not a zero!") }
//! #     }
//! # }
//! # use rustler::{Env, OwnedBinary, Binary, NifResult, Error};
//! #[rustler::nif]
//! fn wrapper_for_some_<'a>(env: Env<'a>) -> NifResult<Binary<'a>> {
//!     let mut owned = OwnedBinary::new(100).ok_or(Error::Term(Box::new("no mem")))?;
//!     for byte in owned.as_mut_slice() {
//!         *byte = 0;
//!     }
//!
//!     // Some third party API which requires the slice to be all zeros on entry.
//!     some_third_party_api(owned.as_mut_slice());
//!
//!     // The imaginary API call presumedly filled in our binary with meaningful
//!     // data, so let's return it.
//!     Ok(Binary::from_owned(owned, env))
//! }
//!
//! ```
//!
//! [`Binary`]: struct.Binary.html
//! [`Env`]: ../../env/struct.Env.html
//! [`OwnedBinary`]: struct.OwnedBinary.html

use crate::{
    wrapper::binary::{alloc, realloc, ErlNifBinary},
    Decoder, Encoder, Env, Error, NifResult, Term,
};
use std::{
    borrow::{Borrow, BorrowMut},
    io::Write,
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
};

/// An mutable smart-pointer to an Erlang binary.
///
/// See [module-level doc](index.html) for more information.
pub struct OwnedBinary(ErlNifBinary);

impl<'a> OwnedBinary {
    pub unsafe fn from_raw(inner: ErlNifBinary) -> OwnedBinary {
        OwnedBinary(inner)
    }

    /// Allocates a new `OwnedBinary` with size `size`.
    ///
    /// Memory is not initialized. If uninitialized memory is undesirable, set it
    /// manually.
    ///
    /// # Errors
    ///
    /// If allocation fails, `None` is returned.
    pub fn new(size: usize) -> Option<OwnedBinary> {
        unsafe { alloc(size) }.map(OwnedBinary)
    }

    /// Copies `src`'s data into a new `OwnedBinary`.
    ///
    /// # Errors
    ///
    /// If allocation fails, `None` is returned.
    pub fn from_unowned(src: &Binary) -> Option<OwnedBinary> {
        OwnedBinary::new(src.len()).map(|mut b| {
            b.as_mut_slice().copy_from_slice(&src);
            b
        })
    }

    /// Attempts to reallocate `self` with the new size.
    ///
    /// Memory outside the range of the original binary will not be initialized. If
    /// uninitialized memory is undesirable, set it manually.
    ///
    /// # Errors
    ///
    /// If reallocation fails, `false` is returned. Data remains intact on error.
    #[must_use]
    pub fn realloc(&mut self, size: usize) -> bool {
        unsafe { realloc(&mut self.0, size) }
    }

    /// Attempts to reallocate `self` with the new size.
    ///
    /// If reallocation fails, it will perform a copy instead.
    ///
    /// Memory outside the range of the original binary will not be initialized. If
    /// uninitialized memory is undesirable, set it manually.
    pub fn realloc_or_copy(&mut self, size: usize) {
        if !self.realloc(size) {
            let mut new = OwnedBinary::new(size).unwrap();
            if let Ok(num_written) = new.as_mut_slice().write(self.as_slice()) {
                if !(num_written == self.len() || num_written == new.len()) {
                    panic!("Could not copy binary");
                }
                ::std::mem::swap(&mut self.0, &mut new.0);
            } else {
                panic!("Could not copy binary");
            }
        }
    }

    /// Extracts a slice containing the entire binary.
    pub fn as_slice(&self) -> &[u8] {
        unsafe { ::std::slice::from_raw_parts(self.0.data, self.0.size) }
    }

    /// Extracts a mutable slice of the entire binary.
    pub fn as_mut_slice(&mut self) -> &mut [u8] {
        unsafe { ::std::slice::from_raw_parts_mut(self.0.data, self.0.size) }
    }

    /// Consumes `self` and returns an immutable `Binary`.
    ///
    /// This method is the mirror of [`Binary::from_owned`], and they can be used
    /// interchangeably.
    ///
    /// [`Binary::from_owned`]: struct.Binary.html#method.from_owned
    pub fn release(self, env: Env) -> Binary {
        Binary::from_owned(self, env)
    }
}

impl Borrow<[u8]> for OwnedBinary {
    fn borrow(&self) -> &[u8] {
        self.as_slice()
    }
}
impl BorrowMut<[u8]> for OwnedBinary {
    fn borrow_mut(&mut self) -> &mut [u8] {
        self.as_mut_slice()
    }
}
impl Deref for OwnedBinary {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        self.as_slice()
    }
}
impl DerefMut for OwnedBinary {
    fn deref_mut(&mut self) -> &mut [u8] {
        self.as_mut_slice()
    }
}

impl Drop for OwnedBinary {
    fn drop(&mut self) {
        unsafe { rustler_sys::enif_release_binary(&mut self.0) };
    }
}

unsafe impl Send for OwnedBinary {}

/// An immutable smart-pointer to an Erlang binary.
///
/// See [module-level doc](index.html) for more information.
#[derive(Copy, Clone)]
pub struct Binary<'a> {
    inner: ErlNifBinary,
    term: Term<'a>,
}

impl<'a> Binary<'a> {
    /// Consumes `owned` and returns an immutable `Binary`.
    pub fn from_owned(owned: OwnedBinary, env: Env<'a>) -> Self {
        // We are transferring ownership of `owned`'s data to the
        // environment. Therefore, we need to prevent `owned`'s destructor being
        // called at the end of this scope. The least error-prone solution (compared
        // to `mem::forget()`) is to wrap `owned` in a `ManuallyDrop` and EXPLICITLY
        // NOT CALL `ManuallyDrop::drop()`.
        let mut owned = std::mem::ManuallyDrop::new(owned);
        let term = unsafe {
            Term::new(
                env,
                rustler_sys::enif_make_binary(env.as_c_arg(), &mut owned.0),
            )
        };
        Binary {
            inner: owned.0,
            term,
        }
    }

    /// Copies `self`'s data into a new `OwnedBinary`.
    ///
    /// # Errors
    ///
    /// If allocation fails, an error will be returned.
    pub fn to_owned(&self) -> Option<OwnedBinary> {
        OwnedBinary::from_unowned(self)
    }

    /// Creates a `Binary` from `term`.
    ///
    /// # Errors
    ///
    /// If `term` is not a binary, an error will be returned.
    pub fn from_term(term: Term<'a>) -> Result<Self, Error> {
        let mut binary = MaybeUninit::uninit();
        if unsafe {
            rustler_sys::enif_inspect_binary(
                term.get_env().as_c_arg(),
                term.as_c_arg(),
                binary.as_mut_ptr(),
            )
        } == 0
        {
            return Err(Error::BadArg);
        }
        Ok(Binary {
            inner: unsafe { binary.assume_init() },
            term,
        })
    }

    /// Creates a `Binary` from `term`.
    ///
    /// # Errors
    ///
    /// If `term` is not an `iolist`, an error will be returned.
    pub fn from_iolist(term: Term<'a>) -> Result<Self, Error> {
        let mut binary = MaybeUninit::uninit();
        if unsafe {
            rustler_sys::enif_inspect_iolist_as_binary(
                term.get_env().as_c_arg(),
                term.as_c_arg(),
                binary.as_mut_ptr(),
            )
        } == 0
        {
            return Err(Error::BadArg);
        }
        Ok(Binary {
            inner: unsafe { binary.assume_init() },
            term,
        })
    }

    /// Returns an Erlang term representation of `self`.
    pub fn to_term<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.term.in_env(env)
    }

    /// Extracts a slice containing the entire binary.
    pub fn as_slice(&self) -> &'a [u8] {
        unsafe { ::std::slice::from_raw_parts(self.inner.data, self.inner.size) }
    }

    /// Returns a new view into the same binary.
    ///
    /// This method is analogous to subslicing (e.g. `some_data[offset..length]`) in
    /// that it does not copy nor allocate data.
    ///
    /// # Errors
    ///
    /// If `offset + length` is out of bounds, an error will be returned.
    pub fn make_subbinary(&self, offset: usize, length: usize) -> NifResult<Binary<'a>> {
        let min_len = length.checked_add(offset);
        if min_len.ok_or(Error::BadArg)? > self.inner.size {
            return Err(Error::BadArg);
        }

        let raw_term = unsafe {
            rustler_sys::enif_make_sub_binary(
                self.term.get_env().as_c_arg(),
                self.term.as_c_arg(),
                offset,
                length,
            )
        };
        let term = unsafe { Term::new(self.term.get_env(), raw_term) };
        // This should never fail, as we are always passing in a binary term.
        Ok(Binary::from_term(term).ok().unwrap())
    }
}

impl<'a> Borrow<[u8]> for Binary<'a> {
    fn borrow(&self) -> &[u8] {
        self.as_slice()
    }
}
impl<'a> Deref for Binary<'a> {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        self.as_slice()
    }
}

impl<'a> Decoder<'a> for Binary<'a> {
    fn decode(term: Term<'a>) -> Result<Self, Error> {
        Binary::from_term(term)
    }
}
impl<'a> Encoder for Binary<'a> {
    fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
        self.to_term(env)
    }
}

/// ## Binary terms
impl<'a> Term<'a> {
    pub fn into_binary(self) -> NifResult<Binary<'a>> {
        Binary::from_term(self)
    }
}
<|endoftext|>


<commit_before>extern crate frippy;
extern crate time;
extern crate tokio_core;
extern crate glob;
extern crate futures;

#[cfg(feature = "mysql")]
#[macro_use]
extern crate diesel_migrations;
#[cfg(feature = "mysql")]
extern crate diesel;

#[macro_use]
extern crate log;

use std::collections::HashMap;

use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

use tokio_core::reactor::Core;
use futures::future;
use glob::glob;

use frippy::plugins;
use frippy::Config;

#[cfg(feature = "mysql")]
embed_migrations!();

struct Logger;

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.target().contains("frippy")
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            if record.metadata().level() >= LogLevel::Debug {
                println!("[{}]({}) {} -> {}",
                         time::now().rfc822(),
                         record.level(),
                         record.target(),
                         record.args());
            } else {
                println!("[{}]({}) {}",
                         time::now().rfc822(),
                         record.level(),
                         record.args());
            }
        }
    }
}

fn main() {
    let log_level = if cfg!(debug_assertions) {
        LogLevelFilter::Debug
    } else {
        LogLevelFilter::Info
    };

    log::set_logger(|max_log_level| {
                        max_log_level.set(log_level);
                        Box::new(Logger)
                    })
            .unwrap();

    // Load all toml files in the configs directory
    let mut configs = Vec::new();
    for toml in glob("configs/*.toml").unwrap() {
        match toml {
            Ok(path) => {
                info!("Loading {}", path.to_str().unwrap());
                match Config::load(path) {
                    Ok(v) => configs.push(v),
                    Err(e) => error!("Incorrect config file {}", e),
                }
            }
            Err(e) => error!("Failed to read path {}", e),
        }
    }

    // Without configs the bot would just idle
    if configs.is_empty() {
        error!("No config file found");
        return;
    }

    // Create an event loop to run the connections on.
    let mut reactor = Core::new().unwrap();

    // Open a connection and add work for each config
    for config in configs {

        let mut disabled_plugins = None;
        let mut mysql_url = None;
        if let &Some(ref options) = &config.options {
            if let Some(disabled) = options.get("disabled_plugins") {
                disabled_plugins = Some(disabled
                                            .split(",")
                                            .map(|p| p.trim())
                                            .collect::<Vec<_>>());
            }

            mysql_url = options.get("mysql_url");
        }

        let mut bot = frippy::Bot::new();
        bot.add_plugin(plugins::Help::new());
        bot.add_plugin(plugins::Url::new(1024));
        bot.add_plugin(plugins::Emoji::new());
        bot.add_plugin(plugins::Currency::new());
        bot.add_plugin(plugins::KeepNick::new());
        #[cfg(feature = "mysql")]
        {
            if let Some(url) = mysql_url {
                use diesel;
                use diesel::Connection;
                match diesel::mysql::MysqlConnection::establish(url) {
                    Ok(conn) => {
                        embedded_migrations::run(&conn).unwrap();
                        bot.add_plugin(plugins::Factoids::new(conn));
                    }
                    Err(e) => error!("Failed to connect to database: {}", e),
                }
            } else {
                bot.add_plugin(plugins::Factoids::new(HashMap::new()));
            }
        }
        #[cfg(not(feature = "mysql"))]
        {
            if let Some(_) = mysql_url {
                error!("frippy was not built with the mysql feature")
            }
            bot.add_plugin(plugins::Factoids::new(HashMap::new()));
        }

        if let Some(disabled_plugins) = disabled_plugins {
            for name in disabled_plugins {
                if let None = bot.remove_plugin(name) {
                    error!("{:?} was not found - could not disable", name);
                }
            }
        }

        bot.connect(&mut reactor, &config);
    }

    // Run the main loop forever
    reactor.run(future::empty::<(), ()>()).unwrap();
}
<commit_msg>Switch to HashMap if the migrations failed to run<commit_after>extern crate frippy;
extern crate time;
extern crate tokio_core;
extern crate glob;
extern crate futures;

#[cfg(feature = "mysql")]
#[macro_use]
extern crate diesel_migrations;
#[cfg(feature = "mysql")]
extern crate diesel;

#[macro_use]
extern crate log;

use std::collections::HashMap;

use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

use tokio_core::reactor::Core;
use futures::future;
use glob::glob;

use frippy::plugins;
use frippy::Config;

#[cfg(feature = "mysql")]
embed_migrations!();

struct Logger;

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.target().contains("frippy")
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            if record.metadata().level() >= LogLevel::Debug {
                println!("[{}]({}) {} -> {}",
                         time::now().rfc822(),
                         record.level(),
                         record.target(),
                         record.args());
            } else {
                println!("[{}]({}) {}",
                         time::now().rfc822(),
                         record.level(),
                         record.args());
            }
        }
    }
}

fn main() {
    let log_level = if cfg!(debug_assertions) {
        LogLevelFilter::Debug
    } else {
        LogLevelFilter::Info
    };

    log::set_logger(|max_log_level| {
                        max_log_level.set(log_level);
                        Box::new(Logger)
                    })
            .unwrap();

    // Load all toml files in the configs directory
    let mut configs = Vec::new();
    for toml in glob("configs/*.toml").unwrap() {
        match toml {
            Ok(path) => {
                info!("Loading {}", path.to_str().unwrap());
                match Config::load(path) {
                    Ok(v) => configs.push(v),
                    Err(e) => error!("Incorrect config file {}", e),
                }
            }
            Err(e) => error!("Failed to read path {}", e),
        }
    }

    // Without configs the bot would just idle
    if configs.is_empty() {
        error!("No config file found");
        return;
    }

    // Create an event loop to run the connections on.
    let mut reactor = Core::new().unwrap();

    // Open a connection and add work for each config
    for config in configs {

        let mut disabled_plugins = None;
        let mut mysql_url = None;
        if let &Some(ref options) = &config.options {
            if let Some(disabled) = options.get("disabled_plugins") {
                disabled_plugins = Some(disabled
                                            .split(",")
                                            .map(|p| p.trim())
                                            .collect::<Vec<_>>());
            }

            mysql_url = options.get("mysql_url");
        }

        let mut bot = frippy::Bot::new();
        bot.add_plugin(plugins::Help::new());
        bot.add_plugin(plugins::Url::new(1024));
        bot.add_plugin(plugins::Emoji::new());
        bot.add_plugin(plugins::Currency::new());
        bot.add_plugin(plugins::KeepNick::new());
        #[cfg(feature = "mysql")]
        {
            if let Some(url) = mysql_url {
                use diesel;
                use diesel::Connection;
                match diesel::mysql::MysqlConnection::establish(url) {
                    Ok(conn) => {
                        match embedded_migrations::run(&conn) {
                            Ok(_) => bot.add_plugin(plugins::Factoids::new(conn)),
                            Err(e) => {
                                bot.add_plugin(plugins::Factoids::new(HashMap::new()));
                                error!("Failed to run migrations: {}", e);
                            }
                        }
                    }
                    Err(e) => error!("Failed to connect to database: {}", e),
                }
            } else {
                bot.add_plugin(plugins::Factoids::new(HashMap::new()));
            }
        }
        #[cfg(not(feature = "mysql"))]
        {
            if let Some(_) = mysql_url {
                error!("frippy was not built with the mysql feature")
            }
            bot.add_plugin(plugins::Factoids::new(HashMap::new()));
        }

        if let Some(disabled_plugins) = disabled_plugins {
            for name in disabled_plugins {
                if let None = bot.remove_plugin(name) {
                    error!("{:?} was not found - could not disable", name);
                }
            }
        }

        bot.connect(&mut reactor, &config);
    }

    // Run the main loop forever
    reactor.run(future::empty::<(), ()>()).unwrap();
}
<|endoftext|>


<commit_before>//! [json_typegen](https://typegen.vestera.as/) as just a library,
//! for use in build scripts and other crates.
//! If you want an actual interface, like a website, CLI or procedural macro, check the repo:
//! [github.com/evestera/json_typegen](https://github.com/evestera/json_typegen)
//!
//! Note: This crate is to a certain extent considered internal API of the `json_typegen` tools.
//! If you want to use this crate directly, be prepared for breaking changes to happen, and consider
//! [opening an issue](https://github.com/evestera/json_typegen/issues/new)
//! to let me know what you are using. (Breaking changes may still happen,
//! but then I'll at least try to keep your use-case in mind if possible.
//! This has happened enough by now that there are parts I already consider public API.)

#[cfg(feature = "local-samples")]
use std::fs::File;

use lazy_static::lazy_static;
use regex::Regex;
use serde_json::Value;
use thiserror::Error;

mod generation;
mod hints;
mod inference;
mod options;
#[cfg(feature = "option-parsing")]
pub mod parse;
mod shape;
mod unwrap;
mod util;

use crate::hints::Hints;
pub use crate::options::{ImportStyle, Options, OutputMode};
pub use crate::shape::Shape;
use crate::shape::fold_shapes;

/// The errors that json_typegen_shared may produce
///
/// No stability guarantees are made with for this type
/// except that it is a type that implements `std::error::Error`
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum JTError {
    #[cfg(feature = "remote-samples")]
    #[error("An error occurred while fetching JSON")]
    SampleFetchingError(#[from] ::reqwest::Error),
    #[cfg(feature = "local-samples")]
    #[error("An error occurred while reading JSON from file")]
    SampleReadingError(#[from] ::std::io::Error),
    #[error("An error occurred while parsing JSON")]
    JsonParsingError(#[from] ::serde_json::Error),
    #[error("An error occurred while parsing a macro or macro input {0}")]
    MacroParsingError(String),
}

enum SampleSource<'a> {
    #[cfg(feature = "remote-samples")]
    Url(&'a str),
    #[cfg(feature = "local-samples")]
    File(&'a str),
    Text(&'a str),
}

#[cfg(feature = "option-parsing")]
/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input)
        .map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

#[cfg(feature = "option-parsing")]
/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input)
        .map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let sample = get_and_parse_sample(&source)?;
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let inferred: Vec<Shape> = crate::unwrap::unwrap(&options.unwrap, sample)
        .iter()
        .map(|val| inference::value_to_shape(val, &hints))
        .collect();
    let shape = fold_shapes(inferred);

    codegen_from_shape(name, &shape, options)
}

/// Just code generation, no inference
pub fn codegen_from_shape(name: &str, shape: &Shape, options: Options) -> Result<String, JTError> {
    let mut generated_code = if options.runnable {
        generation::rust::rust_program(name, shape, options)
    } else {
        match options.output_mode {
            OutputMode::Rust => generation::rust::rust_types(name, shape, options),
            OutputMode::JsonSchema => generation::json_schema::json_schema(name, shape, options),
            OutputMode::KotlinJackson | OutputMode::KotlinKotlinx => {
                generation::kotlin::kotlin_types(name, shape, options)
            }
            OutputMode::Shape => generation::shape::shape_string(name, shape, options),
            OutputMode::Typescript => {
                generation::typescript::typescript_types(name, shape, options)
            }
            OutputMode::TypescriptTypeAlias => {
                generation::typescript_type_alias::typescript_type_alias(name, shape, options)
            }
        }
    };

    // Ensure generated code ends with exactly one newline
    generated_code.truncate(generated_code.trim_end().len());
    generated_code.push('\n');

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    lazy_static! {
        static ref PUB_RE: Regex = Regex::new(
            r"(?x)
                pub ( \( (?P<restriction> [^)]+ ) \) )?
                \s+
                (?P<name> .+ )
            "
        )
        .unwrap();
    }
    match PUB_RE.captures(name) {
        Some(captures) => {
            options.type_visibility = match captures.name("restriction") {
                Some(restriction) => format!("pub({})", restriction.as_str()),
                None => "pub".into(),
            };
            captures.name("name").unwrap().as_str()
        }
        None => {
            // If there is no visibility specified here, we want to use whatever is set elsewhere
            name
        }
    }
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    #[cfg(feature = "remote-samples")]
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    #[cfg(feature = "local-samples")]
    return SampleSource::File(s);
    #[cfg(not(feature = "local-samples"))]
    return SampleSource::Text(s);
}

fn get_and_parse_sample(source: &SampleSource) -> Result<Value, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => serde_json::de::from_reader(
            reqwest::get(url)?.error_for_status()?
        ),

        #[cfg(feature = "local-samples")]
        SampleSource::File(path) => serde_json::de::from_reader(File::open(path)?),

        SampleSource::Text(text) => serde_json::from_str(text),
    };
    Ok(parse_result?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<commit_msg>Expose StringTransform<commit_after>//! [json_typegen](https://typegen.vestera.as/) as just a library,
//! for use in build scripts and other crates.
//! If you want an actual interface, like a website, CLI or procedural macro, check the repo:
//! [github.com/evestera/json_typegen](https://github.com/evestera/json_typegen)
//!
//! Note: This crate is to a certain extent considered internal API of the `json_typegen` tools.
//! If you want to use this crate directly, be prepared for breaking changes to happen, and consider
//! [opening an issue](https://github.com/evestera/json_typegen/issues/new)
//! to let me know what you are using. (Breaking changes may still happen,
//! but then I'll at least try to keep your use-case in mind if possible.
//! This has happened enough by now that there are parts I already consider public API.)

#[cfg(feature = "local-samples")]
use std::fs::File;

use lazy_static::lazy_static;
use regex::Regex;
use serde_json::Value;
use thiserror::Error;

mod generation;
mod hints;
mod inference;
mod options;
#[cfg(feature = "option-parsing")]
pub mod parse;
mod shape;
mod unwrap;
mod util;

use crate::hints::Hints;
pub use crate::options::{ImportStyle, Options, OutputMode, StringTransform};
pub use crate::shape::Shape;
use crate::shape::fold_shapes;

/// The errors that json_typegen_shared may produce
///
/// No stability guarantees are made with for this type
/// except that it is a type that implements `std::error::Error`
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum JTError {
    #[cfg(feature = "remote-samples")]
    #[error("An error occurred while fetching JSON")]
    SampleFetchingError(#[from] ::reqwest::Error),
    #[cfg(feature = "local-samples")]
    #[error("An error occurred while reading JSON from file")]
    SampleReadingError(#[from] ::std::io::Error),
    #[error("An error occurred while parsing JSON")]
    JsonParsingError(#[from] ::serde_json::Error),
    #[error("An error occurred while parsing a macro or macro input {0}")]
    MacroParsingError(String),
}

enum SampleSource<'a> {
    #[cfg(feature = "remote-samples")]
    Url(&'a str),
    #[cfg(feature = "local-samples")]
    File(&'a str),
    Text(&'a str),
}

#[cfg(feature = "option-parsing")]
/// Generate code from a `json_typegen` macro invocation
pub fn codegen_from_macro(input: &str) -> Result<String, JTError> {
    let macro_input = parse::full_macro(input)
        .map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

#[cfg(feature = "option-parsing")]
/// Generate code from the arguments to a `json_typegen` macro invocation
pub fn codegen_from_macro_input(input: &str) -> Result<String, JTError> {
    let macro_input = parse::macro_input(input)
        .map_err(JTError::MacroParsingError)?;

    codegen(
        &macro_input.name,
        &macro_input.sample_source,
        macro_input.options,
    )
}

/// The main code generation function for `json_typegen`
pub fn codegen(name: &str, input: &str, mut options: Options) -> Result<String, JTError> {
    let source = infer_source_type(input);
    let sample = get_and_parse_sample(&source)?;
    let name = handle_pub_in_name(name, &mut options);

    let mut hints_vec = Vec::new();
    std::mem::swap(&mut options.hints, &mut hints_vec);

    let mut hints = Hints::new();
    for (pointer, hint) in hints_vec.iter() {
        hints.add(pointer, hint);
    }

    let inferred: Vec<Shape> = crate::unwrap::unwrap(&options.unwrap, sample)
        .iter()
        .map(|val| inference::value_to_shape(val, &hints))
        .collect();
    let shape = fold_shapes(inferred);

    codegen_from_shape(name, &shape, options)
}

/// Just code generation, no inference
pub fn codegen_from_shape(name: &str, shape: &Shape, options: Options) -> Result<String, JTError> {
    let mut generated_code = if options.runnable {
        generation::rust::rust_program(name, shape, options)
    } else {
        match options.output_mode {
            OutputMode::Rust => generation::rust::rust_types(name, shape, options),
            OutputMode::JsonSchema => generation::json_schema::json_schema(name, shape, options),
            OutputMode::KotlinJackson | OutputMode::KotlinKotlinx => {
                generation::kotlin::kotlin_types(name, shape, options)
            }
            OutputMode::Shape => generation::shape::shape_string(name, shape, options),
            OutputMode::Typescript => {
                generation::typescript::typescript_types(name, shape, options)
            }
            OutputMode::TypescriptTypeAlias => {
                generation::typescript_type_alias::typescript_type_alias(name, shape, options)
            }
        }
    };

    // Ensure generated code ends with exactly one newline
    generated_code.truncate(generated_code.trim_end().len());
    generated_code.push('\n');

    Ok(generated_code)
}

/// Parse "names" like `pub(crate) Foo` into a name and a visibility option
fn handle_pub_in_name<'a>(name: &'a str, options: &mut Options) -> &'a str {
    lazy_static! {
        static ref PUB_RE: Regex = Regex::new(
            r"(?x)
                pub ( \( (?P<restriction> [^)]+ ) \) )?
                \s+
                (?P<name> .+ )
            "
        )
        .unwrap();
    }
    match PUB_RE.captures(name) {
        Some(captures) => {
            options.type_visibility = match captures.name("restriction") {
                Some(restriction) => format!("pub({})", restriction.as_str()),
                None => "pub".into(),
            };
            captures.name("name").unwrap().as_str()
        }
        None => {
            // If there is no visibility specified here, we want to use whatever is set elsewhere
            name
        }
    }
}

fn infer_source_type(s: &str) -> SampleSource {
    let s = s.trim();
    if s.starts_with('{') || s.starts_with('[') {
        return SampleSource::Text(s);
    }
    #[cfg(feature = "remote-samples")]
    if s.starts_with("http://") || s.starts_with("https://") {
        return SampleSource::Url(s);
    }
    #[cfg(feature = "local-samples")]
    return SampleSource::File(s);
    #[cfg(not(feature = "local-samples"))]
    return SampleSource::Text(s);
}

fn get_and_parse_sample(source: &SampleSource) -> Result<Value, JTError> {
    let parse_result = match *source {
        #[cfg(feature = "remote-samples")]
        SampleSource::Url(url) => serde_json::de::from_reader(
            reqwest::get(url)?.error_for_status()?
        ),

        #[cfg(feature = "local-samples")]
        SampleSource::File(path) => serde_json::de::from_reader(File::open(path)?),

        SampleSource::Text(text) => serde_json::from_str(text),
    };
    Ok(parse_result?)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_handle_pub_in_name() {
        let mut options = Options::default();
        let name = handle_pub_in_name("Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, Options::default().type_visibility);
        let name = handle_pub_in_name("pub Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub".to_string());
        let name = handle_pub_in_name("pub(crate) Foo Bar", &mut options);
        assert_eq!(name, "Foo Bar");
        assert_eq!(options.type_visibility, "pub(crate)".to_string());
        let name = handle_pub_in_name("pub(some::path) Foo", &mut options);
        assert_eq!(name, "Foo");
        assert_eq!(options.type_visibility, "pub(some::path)".to_string());
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 LambdaStack All rights reserved.
//
// 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.

#![allow(unused_imports)]

extern crate ceph_rust;
extern crate libc;

use libc::*;
use std::ffi::{CStr, CString};
use std::{ptr, str, slice};

#[cfg(target_os = "linux")]
use ceph_rust::ceph as ceph_helpers;
#[cfg(target_os = "linux")]
use ceph_rust::rados as ceph;
#[cfg(target_os = "linux")]
use ceph_rust::admin_sockets::*;

use ceph_rust::JsonData;

macro_rules! zeroed_c_char_buf {
	($n:expr) => {
		repeat(0).take($n).collect::<Vec<c_char>>();
	}
}

#[cfg(not(target_os = "linux"))]
fn main() {}

// NB: The examples below show a mix of raw native access and rust specific calls.

#[cfg(target_os = "linux")]
fn main() {
    let mut major: i32 = 0;
    let mut minor: i32 = 0;
    let mut extra: i32 = 0;

    let config_file = CString::new("/etc/ceph/ceph.conf").unwrap();
    let pool_name = CString::new("lsio").unwrap();
    let mut cluster: ceph::rados_t = std::ptr::null_mut();
    let mut ret_code: i32;

    // NB: These examples (except for a few) are low level examples that require the unsafe block.
    // However, work for the higher level pur Rust is being worked on in the ceph.rs module of
    // the library. A few of those are present below. We will create a common Result or Option
    // return and allow for pattern matching.

    // Example of accessing the `Admin Socket` for mon
    match admin_socket_command("help", "/var/run/ceph/ceph-mon.ceph-vm1.asok") {
        Ok(json) => {
            println!("{}", json);
        },
        Err(e) => { println!("{}", e); },
    }

    unsafe {
        ceph::rados_version(&mut major, &mut minor, &mut extra);
        ret_code = ceph::rados_create(&mut cluster, std::ptr::null());
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_conf_read_file(cluster, config_file.as_ptr());
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_connect(cluster);
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_pool_create(cluster, pool_name.as_ptr());
        println!("Return code: {}", ret_code);

        let pools_list = ceph_helpers::rados_pools(cluster).unwrap();
        println!("{:?}", pools_list);

        ret_code = ceph::rados_pool_delete(cluster, pool_name.as_ptr());
        println!("Return code: {}", ret_code);

        let instance_id = ceph::rados_get_instance_id(cluster);
        println!("Instance ID: {}", instance_id);

        let buf_size: usize = 37; // 36 is the constant size +1 for null.
        let mut fs_id: Vec<u8> = Vec::with_capacity(buf_size);

        let len = ceph::rados_cluster_fsid(cluster, fs_id.as_mut_ptr() as *mut i8, buf_size);
        let slice = slice::from_raw_parts(fs_id.as_mut_ptr(), buf_size - 1);
        let s: &str = str::from_utf8(slice).unwrap();
        println!("rados_cluster_fsid len: {} - {}", len, s);

        // Rust specific example...
        let cluster_stat = ceph_helpers::rados_stat_cluster(cluster);
        println!("Cluster stat: {:?}", cluster_stat);

        // Mon command to check the health. Same as `ceph -s`
        match ceph_helpers::ceph_mon_command(cluster, "prefix", "status", None) {
            Ok((outbuf, outs)) => {
                match outbuf {
                    Some(output) => println!("Ceph mon command (outbuf):\n{}", output),
                    None => {},
                }
                match outs {
                    Some(output) => println!("Ceph mon command (outs):\n{}", output),
                    None => {},
                }
            },
            Err(e) => {println!("{:?}", e);},
        }

        // Print CephHealth of cluster
        println!("{}", ceph_helpers::ceph_health_string(cluster).unwrap_or("".to_string()));

        // Print Status of cluster health a different way
        println!("{}", ceph_helpers::ceph_status(cluster, &["health", "overall_status"]).unwrap());

        // Another example
        println!("{:?}", ceph_helpers::ceph_status_string_value(cluster, &["fsid"]));

        // This command encapsulates the lower level mon, osd, pg commands and returns JsonData objects based on the key path
        println!("{:?}", ceph_helpers::ceph_command(cluster, "prefix", "status", ceph_helpers::CephCommandTypes::Mon, &["health"]));

        // Get a list of Ceph librados commands in JSON format.
        // It's very long so it's commented out.
        // println!("{}", ceph_helpers::ceph_commands(cluster, None).unwrap().pretty());

        // Currently - parses the `ceph --version` call. The admin socket commands `version` and `git_version`
        // will be called soon to replace the string parse.
        let ceph_ver = ceph_helpers::ceph_version("/var/run/ceph/ceph-mon.ceph-vm1.asok"); // Change to the real mon admin socket name
        println!("Ceph Version - {:?}", ceph_ver);

        ceph::rados_shutdown(cluster);
    }

    println!("RADOS Version - v{}.{}.{}", major, minor, extra);

}
<commit_msg>Removed a rogue function call<commit_after>// Copyright 2017 LambdaStack All rights reserved.
//
// 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.

#![allow(unused_imports)]

extern crate ceph_rust;
extern crate libc;

use libc::*;
use std::ffi::{CStr, CString};
use std::{ptr, str, slice};

#[cfg(target_os = "linux")]
use ceph_rust::ceph as ceph_helpers;
#[cfg(target_os = "linux")]
use ceph_rust::rados as ceph;
#[cfg(target_os = "linux")]
use ceph_rust::admin_sockets::*;

use ceph_rust::JsonData;

macro_rules! zeroed_c_char_buf {
	($n:expr) => {
		repeat(0).take($n).collect::<Vec<c_char>>();
	}
}

#[cfg(not(target_os = "linux"))]
fn main() {}

// NB: The examples below show a mix of raw native access and rust specific calls.

#[cfg(target_os = "linux")]
fn main() {
    let mut major: i32 = 0;
    let mut minor: i32 = 0;
    let mut extra: i32 = 0;

    let config_file = CString::new("/etc/ceph/ceph.conf").unwrap();
    let pool_name = CString::new("lsio").unwrap();
    let mut cluster: ceph::rados_t = std::ptr::null_mut();
    let mut ret_code: i32;

    // NB: These examples (except for a few) are low level examples that require the unsafe block.
    // However, work for the higher level pur Rust is being worked on in the ceph.rs module of
    // the library. A few of those are present below. We will create a common Result or Option
    // return and allow for pattern matching.

    // Example of accessing the `Admin Socket` for mon
    match admin_socket_command("help", "/var/run/ceph/ceph-mon.ceph-vm1.asok") {
        Ok(json) => {
            println!("{}", json);
        },
        Err(e) => { println!("{}", e); },
    }

    unsafe {
        ceph::rados_version(&mut major, &mut minor, &mut extra);
        ret_code = ceph::rados_create(&mut cluster, std::ptr::null());
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_conf_read_file(cluster, config_file.as_ptr());
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_connect(cluster);
        println!("Return code: {} - {:?}", ret_code, cluster);

        ret_code = ceph::rados_pool_create(cluster, pool_name.as_ptr());
        println!("Return code: {}", ret_code);

        let pools_list = ceph_helpers::rados_pools(cluster).unwrap();
        println!("{:?}", pools_list);

        ret_code = ceph::rados_pool_delete(cluster, pool_name.as_ptr());
        println!("Return code: {}", ret_code);

        let instance_id = ceph::rados_get_instance_id(cluster);
        println!("Instance ID: {}", instance_id);

        let buf_size: usize = 37; // 36 is the constant size +1 for null.
        let mut fs_id: Vec<u8> = Vec::with_capacity(buf_size);

        let len = ceph::rados_cluster_fsid(cluster, fs_id.as_mut_ptr() as *mut i8, buf_size);
        let slice = slice::from_raw_parts(fs_id.as_mut_ptr(), buf_size - 1);
        let s: &str = str::from_utf8(slice).unwrap();
        println!("rados_cluster_fsid len: {} - {}", len, s);

        // Rust specific example...
        let cluster_stat = ceph_helpers::rados_stat_cluster(cluster);
        println!("Cluster stat: {:?}", cluster_stat);

        // Mon command to check the health. Same as `ceph -s`
        match ceph_helpers::ceph_mon_command(cluster, "prefix", "status", None) {
            Ok((outbuf, outs)) => {
                match outbuf {
                    Some(output) => println!("Ceph mon command (outbuf):\n{}", output),
                    None => {},
                }
                match outs {
                    Some(output) => println!("Ceph mon command (outs):\n{}", output),
                    None => {},
                }
            },
            Err(e) => {println!("{:?}", e);},
        }

        // Print CephHealth of cluster
        println!("{}", ceph_helpers::ceph_health_string(cluster).unwrap_or("".to_string()));

        // Print Status of cluster health a different way
        println!("{}", ceph_helpers::ceph_status(cluster, &["health", "overall_status"]).unwrap());

        // This command encapsulates the lower level mon, osd, pg commands and returns JsonData objects based on the key path
        println!("{:?}", ceph_helpers::ceph_command(cluster, "prefix", "status", ceph_helpers::CephCommandTypes::Mon, &["health"]));

        // Get a list of Ceph librados commands in JSON format.
        // It's very long so it's commented out.
        // println!("{}", ceph_helpers::ceph_commands(cluster, None).unwrap().pretty());

        // Currently - parses the `ceph --version` call. The admin socket commands `version` and `git_version`
        // will be called soon to replace the string parse.
        let ceph_ver = ceph_helpers::ceph_version("/var/run/ceph/ceph-mon.ceph-vm1.asok"); // Change to the real mon admin socket name
        println!("Ceph Version - {:?}", ceph_ver);

        ceph::rados_shutdown(cluster);
    }

    println!("RADOS Version - v{}.{}.{}", major, minor, extra);

}
<|endoftext|>


<commit_before>use std::clone::Clone;
use std::cmp::Ordering::Less;
use std::collections::VecDeque;
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::SeekFrom::Start;
use std::io::{BufRead, BufReader, BufWriter, Seek, Write};
use std::marker::PhantomData;
use std::path::PathBuf;

use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json;
use tempdir::TempDir;

use super::{GlobalSettings, Line};

/// Trait for types that can be used by
/// [ExternalSorter](struct.ExternalSorter.html). Must be sortable, cloneable,
/// serializeable, and able to report on it's size
pub trait ExternallySortable: Clone + Serialize + DeserializeOwned {
    /// Get the size, in bytes, of this object (used to constrain the buffer
    /// used in the external sort).
    fn get_size(&self) -> u64;
}

/// Iterator that provides sorted `T`s
pub struct ExtSortedIterator<Line> {
    buffers: Vec<VecDeque<Line>>,
    chunk_offsets: Vec<u64>,
    max_per_chunk: u64,
    chunks: u64,
    tmp_dir: TempDir,
    settings: GlobalSettings,
    failed: bool,
}

impl Iterator for ExtSortedIterator<Line>
where
    Line: ExternallySortable,
{
    type Item = Result<Line, Box<dyn Error>>;

    /// # Errors
    ///
    /// This method can fail due to issues reading intermediate sorted chunks
    /// from disk, or due to serde deserialization issues
    fn next(&mut self) -> Option<Self::Item> {
        if self.failed {
            return None;
        }
        // fill up any empty buffers
        let mut empty = true;
        for chunk_num in 0..self.chunks {
            if self.buffers[chunk_num as usize].is_empty() {
                let mut f = match File::open(self.tmp_dir.path().join(chunk_num.to_string())) {
                    Ok(f) => f,
                    Err(e) => {
                        self.failed = true;
                        return Some(Err(Box::new(e)));
                    }
                };
                match f.seek(Start(self.chunk_offsets[chunk_num as usize])) {
                    Ok(_) => (),
                    Err(e) => {
                        self.failed = true;
                        return Some(Err(Box::new(e)));
                    }
                }
                let bytes_read =
                    match fill_buff(&mut self.buffers[chunk_num as usize], f, self.max_per_chunk) {
                        Ok(bytes_read) => bytes_read,
                        Err(e) => {
                            self.failed = true;
                            return Some(Err(e));
                        }
                    };
                self.chunk_offsets[chunk_num as usize] += bytes_read;
                if !self.buffers[chunk_num as usize].is_empty() {
                    empty = false;
                }
            } else {
                empty = false;
            }
        }
        if empty {
            return None;
        }

        // find the next record to write
        // check is_empty() before unwrap()ing
        let mut idx = 0;
        for chunk_num in 0..self.chunks as usize {
            if !self.buffers[chunk_num].is_empty() {
                if self.buffers[idx].is_empty()
                    || (super::compare_by)(
                        self.buffers[chunk_num].front().unwrap(),
                        self.buffers[idx].front().unwrap(),
                        &self.settings,
                    ) == Less
                {
                    idx = chunk_num;
                }
            }
        }

        // unwrap due to checks above
        let r = self.buffers[idx].pop_front().unwrap();
        Some(Ok(r))
    }
}

/// Perform an external sort on an unsorted stream of incoming data
pub struct ExternalSorter<Line>
where
    Line: ExternallySortable,
{
    tmp_dir: Option<PathBuf>,
    buffer_bytes: u64,
    phantom: PhantomData<Line>,
    settings: GlobalSettings,
}

impl ExternalSorter<Line>
where
    Line: ExternallySortable,
{
    /// Create a new `ExternalSorter` with a specified memory buffer and
    /// temporary directory
    pub fn new(
        buffer_bytes: u64,
        tmp_dir: Option<PathBuf>,
        settings: GlobalSettings,
    ) -> ExternalSorter<Line> {
        ExternalSorter {
            buffer_bytes,
            tmp_dir,
            phantom: PhantomData,
            settings,
        }
    }

    /// Sort (based on `compare`) the `T`s provided by `unsorted` and return an
    /// iterator
    ///
    /// # Errors
    ///
    /// This method can fail due to issues writing intermediate sorted chunks
    /// to disk, or due to serde serialization issues
    pub fn sort_by<I>(
        &self,
        unsorted: I,
        settings: GlobalSettings,
    ) -> Result<ExtSortedIterator<Line>, Box<dyn Error>>
    where
        I: Iterator<Item = Line>,
    {
        let tmp_dir = match self.tmp_dir {
            Some(ref p) => TempDir::new_in(p, "uutils_sort")?,
            None => TempDir::new("uutils_sort")?,
        };
        // creating the thing we need to return first due to the face that we need to
        // borrow tmp_dir and move it out
        let mut iter = ExtSortedIterator {
            buffers: Vec::new(),
            chunk_offsets: Vec::new(),
            max_per_chunk: 0,
            chunks: 0,
            tmp_dir,
            settings,
            failed: false,
        };

        {
            let mut total_read = 0;
            let mut chunk = Vec::new();
            // Initial buffer is specified by user
            let mut adjusted_buffer_size = self.buffer_bytes;

            // make the initial chunks on disk
            for seq in unsorted {
                let seq_size = seq.get_size();
                total_read += seq_size;
                // Grow buffer size for a struct/Line larger than buffer
                adjusted_buffer_size =
                    if adjusted_buffer_size < seq_size {
                        seq_size
                    } else {
                        adjusted_buffer_size
                    };
                chunk.push(seq);

                if total_read >= adjusted_buffer_size {
                    super::sort_by(&mut chunk, &self.settings);
                    self.write_chunk(
                        &iter.tmp_dir.path().join(iter.chunks.to_string()),
                        &mut chunk,
                    )?;
                    chunk.clear();
                    total_read = 0;
                    iter.chunks += 1;
                }
            }
            // write the last chunk
            if chunk.len() > 0 {
                super::sort_by(&mut chunk, &self.settings);
                self.write_chunk(
                    &iter.tmp_dir.path().join(iter.chunks.to_string()),
                    &mut chunk,
                )?;
                iter.chunks += 1;
            }

            // initialize buffers for each chunk
            // iter.max_per_chunk = adjusted_buffer_size
            //       .checked_div(iter.chunks)
            //       .unwrap_or(adjusted_buffer_size);
            //
            //
            //
            iter.max_per_chunk = adjusted_buffer_size;
            iter.buffers = vec![VecDeque::new(); iter.chunks as usize];
            iter.chunk_offsets = vec![0 as u64; iter.chunks as usize];
            for chunk_num in 0..iter.chunks {
                let offset = fill_buff(
                    &mut iter.buffers[chunk_num as usize],
                    File::open(iter.tmp_dir.path().join(chunk_num.to_string()))?,
                    iter.max_per_chunk,
                )?;
                iter.chunk_offsets[chunk_num as usize] = offset;
            }
        }

        Ok(iter)
    }

    fn write_chunk(&self, file: &PathBuf, chunk: &mut Vec<Line>) -> Result<(), Box<dyn Error>> {
        let new_file = OpenOptions::new().create(true).append(true).open(file)?;
        let mut buf_write = Box::new(BufWriter::new(new_file)) as Box<dyn Write>;
        for s in chunk {
            let mut serialized = serde_json::to_string(&s).expect("JSON write error: ");
            serialized.push_str("\n");
            buf_write.write(serialized.as_bytes())?;
        }
        buf_write.flush()?;

        Ok(())
    }
}

fn fill_buff<Line>(
    vec: &mut VecDeque<Line>,
    file: File,
    max_bytes: u64,
) -> Result<u64, Box<dyn Error>>
where
    Line: ExternallySortable,
{
    let mut total_read = 0;
    let mut bytes_read = 0;
    for line in BufReader::new(file).lines() {
        let line_s = line?;
        bytes_read += line_s.len() + 1;
        // This is where the bad stuff happens usually
        let deserialized: Line = serde_json::from_str(&line_s).expect("JSON read error: ");
        total_read += deserialized.get_size();
        vec.push_back(deserialized);
        if total_read > max_bytes {
            break;
        }
    }

    Ok(bytes_read as u64)
}
<commit_msg>Implement a minimum readback buffer<commit_after>use std::clone::Clone;
use std::cmp::Ordering::Less;
use std::collections::VecDeque;
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::SeekFrom::Start;
use std::io::{BufRead, BufReader, BufWriter, Seek, Write};
use std::marker::PhantomData;
use std::path::PathBuf;

use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json;
use tempdir::TempDir;

use super::{GlobalSettings, Line};

/// Trait for types that can be used by
/// [ExternalSorter](struct.ExternalSorter.html). Must be sortable, cloneable,
/// serializeable, and able to report on it's size
pub trait ExternallySortable: Clone + Serialize + DeserializeOwned {
    /// Get the size, in bytes, of this object (used to constrain the buffer
    /// used in the external sort).
    fn get_size(&self) -> u64;
}

/// Iterator that provides sorted `T`s
pub struct ExtSortedIterator<Line> {
    buffers: Vec<VecDeque<Line>>,
    chunk_offsets: Vec<u64>,
    max_per_chunk: u64,
    chunks: u64,
    tmp_dir: TempDir,
    settings: GlobalSettings,
    failed: bool,
}

impl Iterator for ExtSortedIterator<Line>
where
    Line: ExternallySortable,
{
    type Item = Result<Line, Box<dyn Error>>;

    /// # Errors
    ///
    /// This method can fail due to issues reading intermediate sorted chunks
    /// from disk, or due to serde deserialization issues
    fn next(&mut self) -> Option<Self::Item> {
        if self.failed {
            return None;
        }
        // fill up any empty buffers
        let mut empty = true;
        for chunk_num in 0..self.chunks {
            if self.buffers[chunk_num as usize].is_empty() {
                let mut f = match File::open(self.tmp_dir.path().join(chunk_num.to_string())) {
                    Ok(f) => f,
                    Err(e) => {
                        self.failed = true;
                        return Some(Err(Box::new(e)));
                    }
                };
                match f.seek(Start(self.chunk_offsets[chunk_num as usize])) {
                    Ok(_) => (),
                    Err(e) => {
                        self.failed = true;
                        return Some(Err(Box::new(e)));
                    }
                }
                let bytes_read =
                    match fill_buff(&mut self.buffers[chunk_num as usize], f, self.max_per_chunk) {
                        Ok(bytes_read) => bytes_read,
                        Err(e) => {
                            self.failed = true;
                            return Some(Err(e));
                        }
                    };
                self.chunk_offsets[chunk_num as usize] += bytes_read;
                if !self.buffers[chunk_num as usize].is_empty() {
                    empty = false;
                }
            } else {
                empty = false;
            }
        }
        if empty {
            return None;
        }

        // find the next record to write
        // check is_empty() before unwrap()ing
        let mut idx = 0;
        for chunk_num in 0..self.chunks as usize {
            if !self.buffers[chunk_num].is_empty() {
                if self.buffers[idx].is_empty()
                    || (super::compare_by)(
                        self.buffers[chunk_num].front().unwrap(),
                        self.buffers[idx].front().unwrap(),
                        &self.settings,
                    ) == Less
                {
                    idx = chunk_num;
                }
            }
        }

        // unwrap due to checks above
        let r = self.buffers[idx].pop_front().unwrap();
        Some(Ok(r))
    }
}

/// Perform an external sort on an unsorted stream of incoming data
pub struct ExternalSorter<Line>
where
    Line: ExternallySortable,
{
    tmp_dir: Option<PathBuf>,
    buffer_bytes: u64,
    phantom: PhantomData<Line>,
    settings: GlobalSettings,
}

impl ExternalSorter<Line>
where
    Line: ExternallySortable,
{
    /// Create a new `ExternalSorter` with a specified memory buffer and
    /// temporary directory
    pub fn new(
        buffer_bytes: u64,
        tmp_dir: Option<PathBuf>,
        settings: GlobalSettings,
    ) -> ExternalSorter<Line> {
        ExternalSorter {
            buffer_bytes,
            tmp_dir,
            phantom: PhantomData,
            settings,
        }
    }

    /// Sort (based on `compare`) the `T`s provided by `unsorted` and return an
    /// iterator
    ///
    /// # Errors
    ///
    /// This method can fail due to issues writing intermediate sorted chunks
    /// to disk, or due to serde serialization issues
    pub fn sort_by<I>(
        &self,
        unsorted: I,
        settings: GlobalSettings,
    ) -> Result<ExtSortedIterator<Line>, Box<dyn Error>>
    where
        I: Iterator<Item = Line>,
    {
        let tmp_dir = match self.tmp_dir {
            Some(ref p) => TempDir::new_in(p, "uutils_sort")?,
            None => TempDir::new("uutils_sort")?,
        };
        // creating the thing we need to return first due to the face that we need to
        // borrow tmp_dir and move it out
        let mut iter = ExtSortedIterator {
            buffers: Vec::new(),
            chunk_offsets: Vec::new(),
            max_per_chunk: 0,
            chunks: 0,
            tmp_dir,
            settings,
            failed: false,
        };

        {
            let mut total_read = 0;
            let mut chunk = Vec::new();
            // Initial buffer is specified by user
            let mut adjusted_buffer_size = self.buffer_bytes;

            // make the initial chunks on disk
            for seq in unsorted {
                let seq_size = seq.get_size();
                total_read += seq_size;
                // Grow buffer size for a struct/Line larger than buffer
                adjusted_buffer_size =
                    if adjusted_buffer_size < seq_size {
                        seq_size
                    } else {
                        adjusted_buffer_size
                    };
                chunk.push(seq);

                if total_read >= adjusted_buffer_size {
                    super::sort_by(&mut chunk, &self.settings);
                    self.write_chunk(
                        &iter.tmp_dir.path().join(iter.chunks.to_string()),
                        &mut chunk,
                    )?;
                    chunk.clear();
                    total_read = 0;
                    iter.chunks += 1;
                }
            }
            // write the last chunk
            if chunk.len() > 0 {
                super::sort_by(&mut chunk, &self.settings);
                self.write_chunk(
                    &iter.tmp_dir.path().join(iter.chunks.to_string()),
                    &mut chunk,
                )?;
                iter.chunks += 1;
            }

            // initialize buffers for each chunk
            //
            // Having a right sized buffer for each chunk for smallish values seems silly to me?
            //
            // We will have to have the entire iter in memory sometime right?
            // Set minimum to the size of the writer buffer, ~8K
            //
            const MINIMUM_READBACK_BUFFER: u64 = 8200;
            let right_sized_buffer = adjusted_buffer_size
                   .checked_div(iter.chunks)
                   .unwrap_or(adjusted_buffer_size);
            iter.max_per_chunk =
                if right_sized_buffer > MINIMUM_READBACK_BUFFER {
                    right_sized_buffer
                } else {
                    MINIMUM_READBACK_BUFFER
                };
            iter.buffers = vec![VecDeque::new(); iter.chunks as usize];
            iter.chunk_offsets = vec![0 as u64; iter.chunks as usize];
            for chunk_num in 0..iter.chunks {
                let offset = fill_buff(
                    &mut iter.buffers[chunk_num as usize],
                    File::open(iter.tmp_dir.path().join(chunk_num.to_string()))?,
                    iter.max_per_chunk,
                )?;
                iter.chunk_offsets[chunk_num as usize] = offset;
            }
        }

        Ok(iter)
    }

    fn write_chunk(&self, file: &PathBuf, chunk: &mut Vec<Line>) -> Result<(), Box<dyn Error>> {
        let new_file = OpenOptions::new().create(true).append(true).open(file)?;
        let mut buf_write = Box::new(BufWriter::new(new_file)) as Box<dyn Write>;
        for s in chunk {
            let mut serialized = serde_json::to_string(&s).expect("JSON write error: ");
            serialized.push_str("\n");
            buf_write.write(serialized.as_bytes())?;
        }
        buf_write.flush()?;

        Ok(())
    }
}

fn fill_buff<Line>(
    vec: &mut VecDeque<Line>,
    file: File,
    max_bytes: u64,
) -> Result<u64, Box<dyn Error>>
where
    Line: ExternallySortable,
{
    let mut total_read = 0;
    let mut bytes_read = 0;
    for line in BufReader::new(file).lines() {
        let line_s = line?;
        bytes_read += line_s.len() + 1;
        // This is where the bad stuff happens usually
        let deserialized: Line = serde_json::from_str(&line_s).expect("JSON read error: ");
        total_read += deserialized.get_size();
        vec.push_back(deserialized);
        if total_read > max_bytes {
            break;
        }
    }

    Ok(bytes_read as u64)
}
<|endoftext|>


<commit_before>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::BufReader;

fn dump_file(filename: &String) {
    let file = File::open(filename).unwrap();
    let mut reader = BufReader::new(file);
    let mut context = mp4parse::MediaContext::new();
    // Turn on debug output.
    context.trace(true);
    // Read all boxes.
    match mp4parse::read_mp4(&mut reader, &mut context) {
        Ok(_) => {},
        Err(mp4parse::Error::Io(e)) => {
            println!("I/O ERROR: {:?}", e);
            panic!(e);
        },
        Err(e) => {
            println!("ERROR: {:?}", e);
        },
    }
    println!("-- result of parsing '{}' --\n{:?}", filename, context);
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(&filename);
        println!("-- end of '{}' --", filename);
    }
}
<commit_msg>Make dump.rs print a nicer error on file open errors.<commit_after>extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::BufReader;

fn dump_file(filename: &String) {
    let file = match File::open(filename) {
        Ok(file) => file,
        _ => {
            println!("ERROR: invalid path '{}'", filename);
            return;
        }
    };
    let mut reader = BufReader::new(file);
    let mut context = mp4parse::MediaContext::new();
    // Turn on debug output.
    context.trace(true);
    // Read all boxes.
    match mp4parse::read_mp4(&mut reader, &mut context) {
        Ok(_) => {},
        Err(mp4parse::Error::Io(e)) => {
            println!("I/O ERROR: {:?}", e);
            panic!(e);
        },
        Err(e) => {
            println!("ERROR: {:?}", e);
        },
    }
    println!("-- result of parsing '{}' --\n{:?}", filename, context);
}

fn main() {
    for filename in env::args().skip(1) {
        println!("-- dump of '{}' --", filename);
        dump_file(&filename);
        println!("-- end of '{}' --", filename);
    }
}
<|endoftext|>