<commit_before>//! Read configurations files.

#![deny(missing_docs_in_private_items)]

use std::{env, fmt, fs, io, path};
use std::io::Read;
use syntax::{ast, codemap};
use toml;
use std::sync::Mutex;

/// Get the configuration file from arguments.
pub fn file_from_args(
    args: &[codemap::Spanned<ast::NestedMetaItemKind>],
) -> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
    for arg in args.iter().filter_map(|a| a.meta_item()) {
        if arg.name() == "conf_file" {
            return match arg.node {
                ast::MetaItemKind::Word | ast::MetaItemKind::List(_) => {
                    Err(("`conf_file` must be a named value", arg.span))
                },
                ast::MetaItemKind::NameValue(ref value) => if let ast::LitKind::Str(ref file, _) = value.node {
                    Ok(Some(file.to_string().into()))
                } else {
                    Err(("`conf_file` value must be a string", value.span))
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected conf format
    Toml(String),
    /// Type error.
    Type(
        /// The name of the key.
        &'static str,
        /// The expected type.
        &'static str,
        /// The type we got instead.
        &'static str,
    ),
    /// There is an unknown key is the file.
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref err) => err.fmt(f),
            Error::Type(key, expected, got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            },
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Io(e)
    }
}

lazy_static! {
    static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
}

macro_rules! define_Conf {
    ($(#[$doc: meta] ($rust_name: ident, $rust_name_str: expr, $default: expr => $($ty: tt)+),)+) => {
        pub use self::helpers::Conf;
        mod helpers {
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all="kebab-case")]
            #[serde(deny_unknown_fields)]
            pub struct Conf {
                $(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)]
                          pub $rust_name: define_Conf!(TY $($ty)+),)+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }
            $(
                mod $rust_name {
                    use serde;
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D)
                    -> Result<define_Conf!(TY $($ty)+), D::Error> {
                        type T = define_Conf!(TY $($ty)+);
                        Ok(T::deserialize(deserializer).unwrap_or_else(|e| {
                            ::utils::conf::ERRORS.lock().expect("no threading here")
                                                        .push(::utils::conf::Error::Toml(e.to_string()));
                            super::$rust_name()
                        }))
                    }
                }

                fn $rust_name() -> define_Conf!(TY $($ty)+) {
                    define_Conf!(DEFAULT $($ty)+, $default)
                }
            )+
        }
    };

    // hack to convert tts
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => {
        $value.as_integer()
        .iter()
        .filter_map(|&i| if i >= 0 { Some(i as u64) } else { None })
        .next()
    };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_array();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            } else {
                Some(slice.iter().map(|v| v.as_str().expect("already checked").to_owned()).collect())
            }
        } else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    (blacklisted_names, "blacklisted_names", ["foo", "bar", "baz", "quux"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold", 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents", [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub",
        "IPv4", "IPv6",
        "JavaScript",
        "NaN",
        "OAuth",
        "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
    ] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold", 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold", 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold", 5 => u64),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack", 200 => u64),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold", 3 => u64),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a emum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold", 200 => u64),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold", 1 => u64),
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    let mut current = try!(env::current_dir());

    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) => if e.kind() != io::ErrorKind::NotFound {
                    return Err(e);
                },
                _ => (),
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (toml::from_str("").expect("we never error on empty config files"), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
    let path = if let Some(path) = path {
        path
    } else {
        return default(Vec::new());
    };

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                return default(vec![err.into()]);
            }

            buf
        },
        Err(err) => return default(vec![err.into()]),
    };

    assert!(
        ERRORS
            .lock()
            .expect("no threading -> mutex always safe")
            .is_empty()
    );
    match toml::from_str(&file) {
        Ok(toml) => (
            toml,
            ERRORS
                .lock()
                .expect("no threading -> mutex always safe")
                .split_off(0),
        ),
        Err(e) => {
            let mut errors = ERRORS
                .lock()
                .expect("no threading -> mutex always safe")
                .split_off(0);
            errors.push(Error::Toml(e.to_string()));
            default(errors)
        },
    }
}
<commit_msg>Add "NaNs" and "GitLab" to `doc-valid-idents`<commit_after>//! Read configurations files.

#![deny(missing_docs_in_private_items)]

use std::{env, fmt, fs, io, path};
use std::io::Read;
use syntax::{ast, codemap};
use toml;
use std::sync::Mutex;

/// Get the configuration file from arguments.
pub fn file_from_args(
    args: &[codemap::Spanned<ast::NestedMetaItemKind>],
) -> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
    for arg in args.iter().filter_map(|a| a.meta_item()) {
        if arg.name() == "conf_file" {
            return match arg.node {
                ast::MetaItemKind::Word | ast::MetaItemKind::List(_) => {
                    Err(("`conf_file` must be a named value", arg.span))
                },
                ast::MetaItemKind::NameValue(ref value) => if let ast::LitKind::Str(ref file, _) = value.node {
                    Ok(Some(file.to_string().into()))
                } else {
                    Err(("`conf_file` value must be a string", value.span))
                },
            };
        }
    }

    Ok(None)
}

/// Error from reading a configuration file.
#[derive(Debug)]
pub enum Error {
    /// An I/O error.
    Io(io::Error),
    /// Not valid toml or doesn't fit the expected conf format
    Toml(String),
    /// Type error.
    Type(
        /// The name of the key.
        &'static str,
        /// The expected type.
        &'static str,
        /// The type we got instead.
        &'static str,
    ),
    /// There is an unknown key is the file.
    UnknownKey(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Toml(ref err) => err.fmt(f),
            Error::Type(key, expected, got) => {
                write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
            },
            Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Io(e)
    }
}

lazy_static! {
    static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
}

macro_rules! define_Conf {
    ($(#[$doc: meta] ($rust_name: ident, $rust_name_str: expr, $default: expr => $($ty: tt)+),)+) => {
        pub use self::helpers::Conf;
        mod helpers {
            /// Type used to store lint configuration.
            #[derive(Deserialize)]
            #[serde(rename_all="kebab-case")]
            #[serde(deny_unknown_fields)]
            pub struct Conf {
                $(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)]
                          pub $rust_name: define_Conf!(TY $($ty)+),)+
                #[allow(dead_code)]
                #[serde(default)]
                third_party: Option<::toml::Value>,
            }
            $(
                mod $rust_name {
                    use serde;
                    use serde::Deserialize;
                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D)
                    -> Result<define_Conf!(TY $($ty)+), D::Error> {
                        type T = define_Conf!(TY $($ty)+);
                        Ok(T::deserialize(deserializer).unwrap_or_else(|e| {
                            ::utils::conf::ERRORS.lock().expect("no threading here")
                                                        .push(::utils::conf::Error::Toml(e.to_string()));
                            super::$rust_name()
                        }))
                    }
                }

                fn $rust_name() -> define_Conf!(TY $($ty)+) {
                    define_Conf!(DEFAULT $($ty)+, $default)
                }
            )+
        }
    };

    // hack to convert tts
    (TY $ty: ty) => { $ty };

    // how to read the value?
    (CONV i64, $value: expr) => { $value.as_integer() };
    (CONV u64, $value: expr) => {
        $value.as_integer()
        .iter()
        .filter_map(|&i| if i >= 0 { Some(i as u64) } else { None })
        .next()
    };
    (CONV String, $value: expr) => { $value.as_str().map(Into::into) };
    (CONV Vec<String>, $value: expr) => {{
        let slice = $value.as_array();

        if let Some(slice) = slice {
            if slice.iter().any(|v| v.as_str().is_none()) {
                None
            } else {
                Some(slice.iter().map(|v| v.as_str().expect("already checked").to_owned()).collect())
            }
        } else {
            None
        }
    }};

    // provide a nicer syntax to declare the default value of `Vec<String>` variables
    (DEFAULT Vec<String>, $e: expr) => { $e.iter().map(|&e| e.to_owned()).collect() };
    (DEFAULT $ty: ty, $e: expr) => { $e };
}

define_Conf! {
    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
    (blacklisted_names, "blacklisted_names", ["foo", "bar", "baz", "quux"] => Vec<String>),
    /// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold", 25 => u64),
    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
    (doc_valid_idents, "doc_valid_idents", [
        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
        "DirectX",
        "ECMAScript",
        "GPLv2", "GPLv3",
        "GitHub", "GitLab",
        "IPv4", "IPv6",
        "JavaScript",
        "NaN", "NaNs",
        "OAuth",
        "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
        "TrueType",
        "iOS", "macOS",
        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
        "MinGW",
    ] => Vec<String>),
    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
    (too_many_arguments_threshold, "too_many_arguments_threshold", 7 => u64),
    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
    (type_complexity_threshold, "type_complexity_threshold", 250 => u64),
    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
    (single_char_binding_names_threshold, "single_char_binding_names_threshold", 5 => u64),
    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
    (too_large_for_stack, "too_large_for_stack", 200 => u64),
    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
    (enum_variant_name_threshold, "enum_variant_name_threshold", 3 => u64),
    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a emum's variant to avoid box suggestion
    (enum_variant_size_threshold, "enum_variant_size_threshold", 200 => u64),
    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold", 1 => u64),
}

/// Search for the configuration file.
pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
    /// Possible filename to search for.
    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];

    let mut current = try!(env::current_dir());

    loop {
        for config_file_name in &CONFIG_FILE_NAMES {
            let config_file = current.join(config_file_name);
            match fs::metadata(&config_file) {
                // Only return if it's a file to handle the unlikely situation of a directory named
                // `clippy.toml`.
                Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
                // Return the error if it's something other than `NotFound`; otherwise we didn't
                // find the project file yet, and continue searching.
                Err(e) => if e.kind() != io::ErrorKind::NotFound {
                    return Err(e);
                },
                _ => (),
            }
        }

        // If the current directory has no parent, we're done searching.
        if !current.pop() {
            return Ok(None);
        }
    }
}

/// Produces a `Conf` filled with the default values and forwards the errors
///
/// Used internally for convenience
fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
    (toml::from_str("").expect("we never error on empty config files"), errors)
}

/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible.
pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
    let path = if let Some(path) = path {
        path
    } else {
        return default(Vec::new());
    };

    let file = match fs::File::open(path) {
        Ok(mut file) => {
            let mut buf = String::new();

            if let Err(err) = file.read_to_string(&mut buf) {
                return default(vec![err.into()]);
            }

            buf
        },
        Err(err) => return default(vec![err.into()]),
    };

    assert!(
        ERRORS
            .lock()
            .expect("no threading -> mutex always safe")
            .is_empty()
    );
    match toml::from_str(&file) {
        Ok(toml) => (
            toml,
            ERRORS
                .lock()
                .expect("no threading -> mutex always safe")
                .split_off(0),
        ),
        Err(e) => {
            let mut errors = ERRORS
                .lock()
                .expect("no threading -> mutex always safe")
                .split_off(0);
            errors.push(Error::Toml(e.to_string()));
            default(errors)
        },
    }
}
<|endoftext|>


<commit_before>// This whole file is inspired by: https://github.com/jeaye/q3/blob/master/src/client/ui/ttf/renderer.rs
// available under the BSD-3 licence.
// It has been modified to work with gl-rs, nalgebra, and rust-freetype

use na::{Point2, Point3, Vector2};
use rusttype;
use rusttype::gpu_cache::{Cache, CacheBuilder};
use std::rc::Rc;

use context::{Context, Texture};
use resource::{AllocationType, BufferType, Effect, GPUVec, ShaderAttribute, ShaderUniform};
use text::Font;

#[path = "../error.rs"]
mod error;

struct TextRenderContext {
    len: usize,
    scale: f32,
    color: Point3<f32>,
    pos: Point2<f32>,
    font: Rc<Font>,
}

/// A ttf text renderer.
pub struct TextRenderer {
    text: String,
    texture: Texture,
    shader: Effect,
    cache: Cache<'static>,
    invsz: ShaderUniform<Vector2<f32>>,
    tex: ShaderUniform<i32>,
    color: ShaderUniform<Point3<f32>>,
    pos: ShaderAttribute<Point2<f32>>,
    uvs: ShaderAttribute<Point2<f32>>,
    contexts: Vec<TextRenderContext>,
    coords: GPUVec<Point2<f32>>,
}

impl TextRenderer {
    /// Creates a new text renderer with `font` as the default font.
    pub fn new() -> TextRenderer {
        //
        // Create cache.
        //
        let atlas_width = 1024;
        let atlas_height = 1024;
        let cache = Cache::builder()
            .dimensions(atlas_width, atlas_height)
            .build();

        //
        // Create texture.
        //
        let ctxt = Context::get();

        /* We're using 1 byte alignment buffering. */
        verify!(ctxt.pixel_storei(Context::UNPACK_ALIGNMENT, 1));

        let texture = verify!(ctxt
            .create_texture()
            .expect("Font texture creation failed."));
        verify!(ctxt.bind_texture(Context::TEXTURE_2D, Some(&texture)));
        verify!(ctxt.tex_image2d(
            Context::TEXTURE_2D,
            0,
            Context::RED as i32,
            atlas_width as i32,
            atlas_height as i32,
            0,
            Context::RED,
            None
        ));

        /* Clamp to the edge to avoid artifacts when scaling. */
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_S,
            Context::CLAMP_TO_EDGE as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_T,
            Context::CLAMP_TO_EDGE as i32
        ));

        /* Linear filtering usually looks best for text. */
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_MIN_FILTER,
            Context::LINEAR as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_MAG_FILTER,
            Context::LINEAR as i32
        ));

        //
        // Create shader.
        //
        let mut shader = Effect::new_from_str(TEXT_VERTEX_SRC, TEXT_FRAGMENT_SRC);
        shader.use_program();

        TextRenderer {
            text: String::new(),
            cache,
            texture,
            invsz: shader.get_uniform("invsz").expect("Could not find invsz"),
            tex: shader.get_uniform("tex0").expect("Could not find tex0"),
            color: shader.get_uniform("color").expect("Could not find color"),
            pos: shader.get_attrib("pos").expect("Could not find pos"),
            uvs: shader.get_attrib("uvs").expect("Could not find uvs"),
            shader: shader,
            contexts: Vec::new(),
            coords: GPUVec::new(Vec::new(), BufferType::Array, AllocationType::StreamDraw),
        }
    }

    /// Adds a piece of text to be drawn during the next frame. The text is not persistent between
    /// frames. This method must be called for each text to draw, and at each update loop
    /// iteration.
    pub fn draw_text(
        &mut self,
        text: &str,
        pos: &Point2<f32>,
        scale: f32,
        font: &Rc<Font>,
        color: &Point3<f32>,
    ) {
        self.text.push_str(text);
        self.contexts.push(TextRenderContext {
            len: text.len(),
            scale,
            color: *color,
            pos: *pos,
            font: font.clone(),
        })
    }

    /// Actually draws the text.
    pub fn render(&mut self, width: f32, height: f32) {
        if self.contexts.len() == 0 {
            return;
        }

        let ctxt = Context::get();
        self.shader.use_program();

        let _ = verify!(ctxt.polygon_mode(Context::FRONT_AND_BACK, Context::FILL));
        verify!(ctxt.enable(Context::BLEND));
        verify!(ctxt.blend_func(Context::SRC_ALPHA, Context::ONE_MINUS_SRC_ALPHA));
        verify!(ctxt.disable(Context::DEPTH_TEST));

        self.pos.enable();
        self.uvs.enable();
        self.tex.upload(&0);
        self.invsz.upload(&Vector2::new(1.0 / width, -1.0 / height));

        verify!(ctxt.bind_texture(Context::TEXTURE_2D, Some(&self.texture)));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_S,
            Context::CLAMP_TO_EDGE as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_T,
            Context::CLAMP_TO_EDGE as i32
        ));

        let mut pos = 0;

        for context in self.contexts.iter() {
            let scale = rusttype::Scale::uniform(context.scale);
            let vmetrics = context.font.font().v_metrics(scale);
            let line_height = vmetrics.ascent - vmetrics.descent;
            let text = &self.text[pos..pos + context.len];
            let font_uid = Font::uid(&context.font);
            let mut vshift = 0.0;

            for line in text.lines() {
                let orig = rusttype::Point {
                    x: context.pos.x,
                    y: context.pos.y + vshift,
                };

                vshift += line_height as f32;
                let layout = context.font.font().layout(line, scale, orig);

                for glyph in layout {
                    let gly: rusttype::PositionedGlyph<'static> = context
                        .font
                        .font()
                        .glyph(glyph.id())
                        .scaled(glyph.scale())
                        .positioned(glyph.position());
                    self.cache.queue_glyph(font_uid, gly); // FIXME: is the call to `.standalone()` costly?
                }

                let _ = self.cache.cache_queued(|rect, data| {
                    verify!(ctxt.tex_sub_image2d(
                        Context::TEXTURE_2D,
                        0,
                        rect.min.x as i32,
                        rect.min.y as i32,
                        rect.width() as i32,
                        rect.height() as i32,
                        Context::RED,
                        Some(&data)
                    ));
                });

                let layout = context.font.font().layout(line, scale, orig);

                {
                    let coords = self.coords.data_mut().as_mut().unwrap();
                    for glyph in layout {
                        if let Some(Some((tex, rect))) = self.cache.rect_for(font_uid, &glyph).ok()
                        {
                            let min_px = rect.min.x as f32;
                            let min_py = rect.min.y as f32 + vmetrics.ascent;
                            let max_px = rect.max.x as f32;
                            let max_py = rect.max.y as f32 + vmetrics.ascent;

                            coords.push(Point2::new(min_px, min_py));
                            coords.push(Point2::new(tex.min.x, tex.min.y));

                            coords.push(Point2::new(min_px, max_py));
                            coords.push(Point2::new(tex.min.x, tex.max.y));

                            coords.push(Point2::new(max_px, min_py));
                            coords.push(Point2::new(tex.max.x, tex.min.y));

                            coords.push(Point2::new(max_px, min_py));
                            coords.push(Point2::new(tex.max.x, tex.min.y));

                            coords.push(Point2::new(min_px, max_py));
                            coords.push(Point2::new(tex.min.x, tex.max.y));

                            coords.push(Point2::new(max_px, max_py));
                            coords.push(Point2::new(tex.max.x, tex.max.y));
                        }
                    }
                }

                self.pos.bind_sub_buffer(&mut self.coords, 1, 0);
                self.uvs.bind_sub_buffer(&mut self.coords, 1, 1);
                self.color.upload(&context.color);

                verify!(ctxt.draw_arrays(Context::TRIANGLES, 0, (self.coords.len() / 2) as i32));
            }
            pos += context.len;

            self.coords.data_mut().as_mut().unwrap().clear();
        }

        self.pos.disable();
        self.uvs.enable();

        verify!(ctxt.enable(Context::DEPTH_TEST));
        verify!(ctxt.disable(Context::BLEND));

        self.contexts.clear();
        self.text.clear();
    }
}

/// Vertex shader used by the material to display line.
pub static TEXT_VERTEX_SRC: &'static str = A_VERY_LONG_STRING;
/// Fragment shader used by the material to display line.
pub static TEXT_FRAGMENT_SRC: &'static str = ANOTHER_VERY_LONG_STRING;

const A_VERY_LONG_STRING: &'static str = "
#version 100

uniform vec2 invsz;
uniform vec3 color;

attribute vec2 pos;
attribute vec2 uvs;

varying vec2 tex;
varying vec3 Color;

void main() {
    gl_Position = vec4(pos.x * invsz.x - 1.0, pos.y * invsz.y + 1.0, -1.0, 1.0);
    tex         = uvs;
    Color       = color;
}
";

const ANOTHER_VERY_LONG_STRING: &'static str = "
#version 100

#ifdef GL_FRAGMENT_PRECISION_HIGH
   precision highp float;
#else
   precision mediump float;
#endif

uniform sampler2D tex0;

varying vec2 tex;
varying vec3 Color;

void main() {
    gl_FragColor = vec4(Color, texture2D(tex0, tex).r);
}
";
<commit_msg>Reverted some formating which was added by cargo fmt<commit_after>// This whole file is inspired by: https://github.com/jeaye/q3/blob/master/src/client/ui/ttf/renderer.rs
// available under the BSD-3 licence.
// It has been modified to work with gl-rs, nalgebra, and rust-freetype

use na::{Point2, Point3, Vector2};
use rusttype;
use rusttype::gpu_cache::{Cache, CacheBuilder};
use std::rc::Rc;

use context::{Context, Texture};
use resource::{AllocationType, BufferType, Effect, GPUVec, ShaderAttribute, ShaderUniform};
use text::Font;

#[path = "../error.rs"]
mod error;

struct TextRenderContext {
    len: usize,
    scale: f32,
    color: Point3<f32>,
    pos: Point2<f32>,
    font: Rc<Font>,
}

/// A ttf text renderer.
pub struct TextRenderer {
    text: String,
    texture: Texture,
    shader: Effect,
    cache: Cache<'static>,
    invsz: ShaderUniform<Vector2<f32>>,
    tex: ShaderUniform<i32>,
    color: ShaderUniform<Point3<f32>>,
    pos: ShaderAttribute<Point2<f32>>,
    uvs: ShaderAttribute<Point2<f32>>,
    contexts: Vec<TextRenderContext>,
    coords: GPUVec<Point2<f32>>,
}

impl TextRenderer {
    /// Creates a new text renderer with `font` as the default font.
    pub fn new() -> TextRenderer {
        //
        // Create cache.
        //
        let atlas_width = 1024;
        let atlas_height = 1024;
        let cache = Cache::builder()
            .dimensions(atlas_width, atlas_height)
            .build();

        //
        // Create texture.
        //
        let ctxt = Context::get();

        /* We're using 1 byte alignment buffering. */
        verify!(ctxt.pixel_storei(Context::UNPACK_ALIGNMENT, 1));

        let texture = verify!(
            ctxt.create_texture()
                .expect("Font texture creation failed.")
        );
        verify!(ctxt.bind_texture(Context::TEXTURE_2D, Some(&texture)));
        verify!(ctxt.tex_image2d(
            Context::TEXTURE_2D,
            0,
            Context::RED as i32,
            atlas_width as i32,
            atlas_height as i32,
            0,
            Context::RED,
            None
        ));

        /* Clamp to the edge to avoid artifacts when scaling. */
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_S,
            Context::CLAMP_TO_EDGE as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_T,
            Context::CLAMP_TO_EDGE as i32
        ));

        /* Linear filtering usually looks best for text. */
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_MIN_FILTER,
            Context::LINEAR as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_MAG_FILTER,
            Context::LINEAR as i32
        ));

        //
        // Create shader.
        //
        let mut shader = Effect::new_from_str(TEXT_VERTEX_SRC, TEXT_FRAGMENT_SRC);
        shader.use_program();

        TextRenderer {
            text: String::new(),
            cache,
            texture,
            invsz: shader.get_uniform("invsz").expect("Could not find invsz"),
            tex: shader.get_uniform("tex0").expect("Could not find tex0"),
            color: shader.get_uniform("color").expect("Could not find color"),
            pos: shader.get_attrib("pos").expect("Could not find pos"),
            uvs: shader.get_attrib("uvs").expect("Could not find uvs"),
            shader: shader,
            contexts: Vec::new(),
            coords: GPUVec::new(Vec::new(), BufferType::Array, AllocationType::StreamDraw),
        }
    }

    /// Adds a piece of text to be drawn during the next frame. The text is not persistent between
    /// frames. This method must be called for each text to draw, and at each update loop
    /// iteration.
    pub fn draw_text(
        &mut self,
        text: &str,
        pos: &Point2<f32>,
        scale: f32,
        font: &Rc<Font>,
        color: &Point3<f32>,
    ) {
        self.text.push_str(text);
        self.contexts.push(TextRenderContext {
            len: text.len(),
            scale,
            color: *color,
            pos: *pos,
            font: font.clone(),
        })
    }

    /// Actually draws the text.
    pub fn render(&mut self, width: f32, height: f32) {
        if self.contexts.len() == 0 {
            return;
        }

        let ctxt = Context::get();
        self.shader.use_program();

        let _ = verify!(ctxt.polygon_mode(Context::FRONT_AND_BACK, Context::FILL));
        verify!(ctxt.enable(Context::BLEND));
        verify!(ctxt.blend_func(Context::SRC_ALPHA, Context::ONE_MINUS_SRC_ALPHA));
        verify!(ctxt.disable(Context::DEPTH_TEST));

        self.pos.enable();
        self.uvs.enable();
        self.tex.upload(&0);
        self.invsz.upload(&Vector2::new(1.0 / width, -1.0 / height));

        verify!(ctxt.bind_texture(Context::TEXTURE_2D, Some(&self.texture)));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_S,
            Context::CLAMP_TO_EDGE as i32
        ));
        verify!(ctxt.tex_parameteri(
            Context::TEXTURE_2D,
            Context::TEXTURE_WRAP_T,
            Context::CLAMP_TO_EDGE as i32
        ));

        let mut pos = 0;

        for context in self.contexts.iter() {
            let scale = rusttype::Scale::uniform(context.scale);
            let vmetrics = context.font.font().v_metrics(scale);
            let line_height = vmetrics.ascent - vmetrics.descent;
            let text = &self.text[pos..pos + context.len];
            let font_uid = Font::uid(&context.font);
            let mut vshift = 0.0;

            for line in text.lines() {
                let orig = rusttype::Point {
                    x: context.pos.x,
                    y: context.pos.y + vshift,
                };

                vshift += line_height as f32;
                let layout = context.font.font().layout(line, scale, orig);

                for glyph in layout {
                    let gly: rusttype::PositionedGlyph<'static> = context
                        .font
                        .font()
                        .glyph(glyph.id())
                        .scaled(glyph.scale())
                        .positioned(glyph.position());
                    self.cache.queue_glyph(font_uid, gly); // FIXME: is the call to `.standalone()` costly?
                }

                let _ = self.cache.cache_queued(|rect, data| {
                    verify!(ctxt.tex_sub_image2d(
                        Context::TEXTURE_2D,
                        0,
                        rect.min.x as i32,
                        rect.min.y as i32,
                        rect.width() as i32,
                        rect.height() as i32,
                        Context::RED,
                        Some(&data)
                    ));
                });

                let layout = context.font.font().layout(line, scale, orig);

                {
                    let coords = self.coords.data_mut().as_mut().unwrap();
                    for glyph in layout {
                        if let Some(Some((tex, rect))) = self.cache.rect_for(font_uid, &glyph).ok()
                        {
                            let min_px = rect.min.x as f32;
                            let min_py = rect.min.y as f32 + vmetrics.ascent;
                            let max_px = rect.max.x as f32;
                            let max_py = rect.max.y as f32 + vmetrics.ascent;

                            coords.push(Point2::new(min_px, min_py));
                            coords.push(Point2::new(tex.min.x, tex.min.y));

                            coords.push(Point2::new(min_px, max_py));
                            coords.push(Point2::new(tex.min.x, tex.max.y));

                            coords.push(Point2::new(max_px, min_py));
                            coords.push(Point2::new(tex.max.x, tex.min.y));

                            coords.push(Point2::new(max_px, min_py));
                            coords.push(Point2::new(tex.max.x, tex.min.y));

                            coords.push(Point2::new(min_px, max_py));
                            coords.push(Point2::new(tex.min.x, tex.max.y));

                            coords.push(Point2::new(max_px, max_py));
                            coords.push(Point2::new(tex.max.x, tex.max.y));
                        }
                    }
                }

                self.pos.bind_sub_buffer(&mut self.coords, 1, 0);
                self.uvs.bind_sub_buffer(&mut self.coords, 1, 1);
                self.color.upload(&context.color);

                verify!(ctxt.draw_arrays(Context::TRIANGLES, 0, (self.coords.len() / 2) as i32));
            }
            pos += context.len;

            self.coords.data_mut().as_mut().unwrap().clear();
        }

        self.pos.disable();
        self.uvs.enable();

        verify!(ctxt.enable(Context::DEPTH_TEST));
        verify!(ctxt.disable(Context::BLEND));

        self.contexts.clear();
        self.text.clear();
    }
}

/// Vertex shader used by the material to display line.
pub static TEXT_VERTEX_SRC: &'static str = A_VERY_LONG_STRING;
/// Fragment shader used by the material to display line.
pub static TEXT_FRAGMENT_SRC: &'static str = ANOTHER_VERY_LONG_STRING;

const A_VERY_LONG_STRING: &'static str = "
#version 100

uniform vec2 invsz;
uniform vec3 color;

attribute vec2 pos;
attribute vec2 uvs;

varying vec2 tex;
varying vec3 Color;

void main() {
    gl_Position = vec4(pos.x * invsz.x - 1.0, pos.y * invsz.y + 1.0, -1.0, 1.0);
    tex         = uvs;
    Color       = color;
}
";

const ANOTHER_VERY_LONG_STRING: &'static str = "
#version 100

#ifdef GL_FRAGMENT_PRECISION_HIGH
   precision highp float;
#else
   precision mediump float;
#endif

uniform sampler2D tex0;

varying vec2 tex;
varying vec3 Color;

void main() {
    gl_FragColor = vec4(Color, texture2D(tex0, tex).r);
}
";
<|endoftext|>


<commit_before>// Copyright 2017 pdb Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::common::*;
use crate::tpi::data::TypeData;

// References for primitive types:
//
// cvinfo.h provides an enumeration:
//   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L328-L750
//
// pdbparse.cpp describes them as strings:
//   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/pdbdump/pdbdump.cpp#L1896-L1974
//
// The most obscure: MSDN Library October 2001 Disk 2 contains a \MSDN\specs.chm file which contains
// html\S66CD.HTM which actually documents the *format* of the primitive type descriptors rather
// than just listing them. TypeData::Primitive is designed to model the orthogonal information
// encoded into the bits of the TypeIndex rather than exploding the matrix like the reference
// implementations.

/// Represents a primitive type like `void` or `char *`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PrimitiveType {
    /// The kind of the primitive type.
    pub kind: PrimitiveKind,

    /// Pointer indirection applied to the primitive type.
    pub indirection: Option<Indirection>,
}

/// A simple type.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PrimitiveKind {
    /// Void type
    Void,

    /// Character (byte)
    Char,

    /// Unsigned character
    UChar,

    /// "Really a char"
    RChar,

    /// Wide characters, i.e. 16 bits
    WChar,

    /// "Really a 16-bit char"
    RChar16,

    /// "Really a 32-bit char"
    RChar32,

    /// Signed 8-bit integer
    I8,

    /// Unsigned 8-bit integer
    U8,

    /// Signed 16-bit integer
    I16,

    /// Unsigned 16-bit integer
    U16,

    /// Signed 32-bit integer
    I32,

    /// Unsigned 32-bit inteer
    U32,

    /// Signed 64-bit integer
    I64,

    /// Unsigned 64-bit integer
    U64,

    /// Signed 128-bit integer
    I128,

    /// Unsigned 128-bit integer
    U128,

    /// 16-bit floating point
    F16,

    /// 32-bit floating point
    F32,

    /// 32-bit partial precision floating point
    F32PP,

    /// 48-bit floating point
    F48,

    /// 64-bit floating point
    F64,

    /// 80-bit floating point
    F80,

    /// 128-bit floating point
    F128,

    /// 32-bit complex number
    Complex32,

    /// 64-bit complex number
    Complex64,

    /// 80-bit complex number
    Complex80,

    /// 128-bit complex number
    Complex128,

    /// 8-bit boolean value
    Bool8,

    /// 16-bit boolean value
    Bool16,

    /// 32-bit boolean value
    Bool32,

    /// 16-bit boolean value
    Bool64,

    /// Windows `HRESULT` error code.
    ///
    /// See: https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values
    HRESULT,
}

/// Pointer mode of primitive types.
///
/// This is partially overlapping with [`PointerKind`](enum.PointerKind.html) for regular pointer
/// type definitions. While `PointerKind` can specify many more pointer types, including relative
/// pointers, `Indirection` also contains a 128-bit variant.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Indirection {
    /// 16-bit ("near") pointer.
    Near16,
    /// 16:16 far pointer.
    Far16,
    /// 16:16 huge pointer.
    Huge16,
    /// 32-bit pointer.
    Near32,
    /// 48-bit 16:32 pointer.
    Far32,
    /// 64-bit near pointer.
    Near64,
    /// 128-bit near pointer.
    Near128,
}

pub fn type_data_for_primitive(index: TypeIndex) -> Result<TypeData<'static>> {
    // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L326-L750

    // primitives live under 0x1000, and we should never reach here for non-primitive indexes
    assert!(index < TypeIndex(0x1000));

    // indirection is stored in these bits
    let indirection = match index.0 & 0xf00 {
        0x000 => None,
        0x100 => Some(Indirection::Near16),
        0x200 => Some(Indirection::Far16),
        0x300 => Some(Indirection::Huge16),
        0x400 => Some(Indirection::Near32),
        0x500 => Some(Indirection::Far32),
        0x600 => Some(Indirection::Near64),
        0x700 => Some(Indirection::Near128),
        _ => {
            return Err(Error::TypeNotFound(index.0));
        }
    };

    // primitive types are stored in the lowest octet
    // this groups "short" and "16-bit integer" together, but... right? *scratches head*
    let kind = match index.0 & 0xff {
        0x03 => PrimitiveKind::Void,
        0x08 => PrimitiveKind::HRESULT,

        0x10 => PrimitiveKind::Char,
        0x20 => PrimitiveKind::UChar,
        0x68 => PrimitiveKind::I8,
        0x69 => PrimitiveKind::U8,

        0x70 => PrimitiveKind::RChar,
        0x71 => PrimitiveKind::WChar,
        0x7a => PrimitiveKind::RChar16,
        0x7b => PrimitiveKind::RChar32,

        0x11 => PrimitiveKind::I16,
        0x21 => PrimitiveKind::U16,
        0x72 => PrimitiveKind::I16,
        0x73 => PrimitiveKind::U16,

        0x12 => PrimitiveKind::I32,
        0x22 => PrimitiveKind::U32,
        0x74 => PrimitiveKind::I32,
        0x75 => PrimitiveKind::U32,

        0x13 => PrimitiveKind::I64,
        0x23 => PrimitiveKind::U64,
        0x76 => PrimitiveKind::I64,
        0x77 => PrimitiveKind::U64,

        0x14 => PrimitiveKind::I128,
        0x24 => PrimitiveKind::U128,
        0x78 => PrimitiveKind::I128,
        0x79 => PrimitiveKind::U128,

        0x46 => PrimitiveKind::F16,
        0x40 => PrimitiveKind::F32,
        0x45 => PrimitiveKind::F32PP,
        0x44 => PrimitiveKind::F48,
        0x41 => PrimitiveKind::F64,
        0x42 => PrimitiveKind::F80,
        0x43 => PrimitiveKind::F128,

        0x50 => PrimitiveKind::Complex32,
        0x51 => PrimitiveKind::Complex64,
        0x52 => PrimitiveKind::Complex80,
        0x53 => PrimitiveKind::Complex128,

        0x30 => PrimitiveKind::Bool8,
        0x31 => PrimitiveKind::Bool16,
        0x32 => PrimitiveKind::Bool32,
        0x33 => PrimitiveKind::Bool64,

        _ => {
            return Err(Error::TypeNotFound(index.0));
        }
    };

    Ok(TypeData::Primitive(PrimitiveType { kind, indirection }))
}
<commit_msg>Add no-type in the primitive types<commit_after>// Copyright 2017 pdb Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::common::*;
use crate::tpi::data::TypeData;

// References for primitive types:
//
// cvinfo.h provides an enumeration:
//   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L328-L750
//
// pdbparse.cpp describes them as strings:
//   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/pdbdump/pdbdump.cpp#L1896-L1974
//
// The most obscure: MSDN Library October 2001 Disk 2 contains a \MSDN\specs.chm file which contains
// html\S66CD.HTM which actually documents the *format* of the primitive type descriptors rather
// than just listing them. TypeData::Primitive is designed to model the orthogonal information
// encoded into the bits of the TypeIndex rather than exploding the matrix like the reference
// implementations.

/// Represents a primitive type like `void` or `char *`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PrimitiveType {
    /// The kind of the primitive type.
    pub kind: PrimitiveKind,

    /// Pointer indirection applied to the primitive type.
    pub indirection: Option<Indirection>,
}

/// A simple type.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PrimitiveKind {
    /// Uncharacterized type (no type)
    NoType,

    /// Void type
    Void,

    /// Character (byte)
    Char,

    /// Unsigned character
    UChar,

    /// "Really a char"
    RChar,

    /// Wide characters, i.e. 16 bits
    WChar,

    /// "Really a 16-bit char"
    RChar16,

    /// "Really a 32-bit char"
    RChar32,

    /// Signed 8-bit integer
    I8,

    /// Unsigned 8-bit integer
    U8,

    /// Signed 16-bit integer
    I16,

    /// Unsigned 16-bit integer
    U16,

    /// Signed 32-bit integer
    I32,

    /// Unsigned 32-bit inteer
    U32,

    /// Signed 64-bit integer
    I64,

    /// Unsigned 64-bit integer
    U64,

    /// Signed 128-bit integer
    I128,

    /// Unsigned 128-bit integer
    U128,

    /// 16-bit floating point
    F16,

    /// 32-bit floating point
    F32,

    /// 32-bit partial precision floating point
    F32PP,

    /// 48-bit floating point
    F48,

    /// 64-bit floating point
    F64,

    /// 80-bit floating point
    F80,

    /// 128-bit floating point
    F128,

    /// 32-bit complex number
    Complex32,

    /// 64-bit complex number
    Complex64,

    /// 80-bit complex number
    Complex80,

    /// 128-bit complex number
    Complex128,

    /// 8-bit boolean value
    Bool8,

    /// 16-bit boolean value
    Bool16,

    /// 32-bit boolean value
    Bool32,

    /// 16-bit boolean value
    Bool64,

    /// Windows `HRESULT` error code.
    ///
    /// See: https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values
    HRESULT,
}

/// Pointer mode of primitive types.
///
/// This is partially overlapping with [`PointerKind`](enum.PointerKind.html) for regular pointer
/// type definitions. While `PointerKind` can specify many more pointer types, including relative
/// pointers, `Indirection` also contains a 128-bit variant.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Indirection {
    /// 16-bit ("near") pointer.
    Near16,
    /// 16:16 far pointer.
    Far16,
    /// 16:16 huge pointer.
    Huge16,
    /// 32-bit pointer.
    Near32,
    /// 48-bit 16:32 pointer.
    Far32,
    /// 64-bit near pointer.
    Near64,
    /// 128-bit near pointer.
    Near128,
}

pub fn type_data_for_primitive(index: TypeIndex) -> Result<TypeData<'static>> {
    // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L326-L750

    // primitives live under 0x1000, and we should never reach here for non-primitive indexes
    assert!(index < TypeIndex(0x1000));

    // indirection is stored in these bits
    let indirection = match index.0 & 0xf00 {
        0x000 => None,
        0x100 => Some(Indirection::Near16),
        0x200 => Some(Indirection::Far16),
        0x300 => Some(Indirection::Huge16),
        0x400 => Some(Indirection::Near32),
        0x500 => Some(Indirection::Far32),
        0x600 => Some(Indirection::Near64),
        0x700 => Some(Indirection::Near128),
        _ => {
            return Err(Error::TypeNotFound(index.0));
        }
    };

    // primitive types are stored in the lowest octet
    // this groups "short" and "16-bit integer" together, but... right? *scratches head*
    let kind = match index.0 & 0xff {
        0x00 => PrimitiveKind::NoType,

        0x03 => PrimitiveKind::Void,
        0x08 => PrimitiveKind::HRESULT,

        0x10 => PrimitiveKind::Char,
        0x20 => PrimitiveKind::UChar,
        0x68 => PrimitiveKind::I8,
        0x69 => PrimitiveKind::U8,

        0x70 => PrimitiveKind::RChar,
        0x71 => PrimitiveKind::WChar,
        0x7a => PrimitiveKind::RChar16,
        0x7b => PrimitiveKind::RChar32,

        0x11 => PrimitiveKind::I16,
        0x21 => PrimitiveKind::U16,
        0x72 => PrimitiveKind::I16,
        0x73 => PrimitiveKind::U16,

        0x12 => PrimitiveKind::I32,
        0x22 => PrimitiveKind::U32,
        0x74 => PrimitiveKind::I32,
        0x75 => PrimitiveKind::U32,

        0x13 => PrimitiveKind::I64,
        0x23 => PrimitiveKind::U64,
        0x76 => PrimitiveKind::I64,
        0x77 => PrimitiveKind::U64,

        0x14 => PrimitiveKind::I128,
        0x24 => PrimitiveKind::U128,
        0x78 => PrimitiveKind::I128,
        0x79 => PrimitiveKind::U128,

        0x46 => PrimitiveKind::F16,
        0x40 => PrimitiveKind::F32,
        0x45 => PrimitiveKind::F32PP,
        0x44 => PrimitiveKind::F48,
        0x41 => PrimitiveKind::F64,
        0x42 => PrimitiveKind::F80,
        0x43 => PrimitiveKind::F128,

        0x50 => PrimitiveKind::Complex32,
        0x51 => PrimitiveKind::Complex64,
        0x52 => PrimitiveKind::Complex80,
        0x53 => PrimitiveKind::Complex128,

        0x30 => PrimitiveKind::Bool8,
        0x31 => PrimitiveKind::Bool16,
        0x32 => PrimitiveKind::Bool32,
        0x33 => PrimitiveKind::Bool64,

        _ => {
            return Err(Error::TypeNotFound(index.0));
        }
    };

    Ok(TypeData::Primitive(PrimitiveType { kind, indirection }))
}
<|endoftext|>


<commit_before>
extern crate rand;
// extern crate time;

// use time::PreciseTime;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::{FileStreamReader, FileStreamWriter};
use std::io::Error;
use tftp_specific::PacketType;
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;
use std::str;
use std::path::Path;

pub struct EndPoint {
    local_connection: SocketAddrV4,
    // A server doesn't need a remote_connection
    remote_connection: Option<SocketAddrV4>,
}

impl EndPoint {
    pub fn new(remote: &str, port: &str, is_server: bool) -> Result<EndPoint, AddrParseError> {

        let ipv4 = try!(get_ip_from(remote));
        if is_server {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 6900),
                remote_connection: None,
            })
        } else {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9990),
                remote_connection: Some(SocketAddrV4::new(ipv4, 6900)),
            })
        }
    }

    pub fn start_listen(&mut self) -> Result<(), Error> {
        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Start servicing requests from a client
        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        let mut last_blk_id = 0u16;
        match buf[1] {
            1 => {
                // RRQ
                println!("RRQ received");
                // Get filename to be sent from the packet
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }
                let filename = str::from_utf8(&buf[2..i]);
                println!("file requested: {}", filename.unwrap());
                // Send data via the socket
                let mut fs = FileStreamReader::new(String::from(filename.unwrap())).unwrap();
                let (mut buf, num_bytes_read) = fs.next().unwrap();
                let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                try!(socket.send_to(v.as_slice(), src));
                // TODO start timer here
                loop {
                    // Get ACK
                    let (amt, src) = try!(socket.recv_from(&mut buf));
                    if buf[1] == 4 {
                        println!("ACK received");
                        last_blk_id += 1;
                        let (buf, num_bytes_read) = fs.next().unwrap();
                        let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                        try!(socket.send_to(v.as_slice(), src));
                    }
                }
            },
            2 => {
                //WRQ
                println!("WRQ received");
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }
                let mut v = vec![];
                v.copy_from_slice(&buf[2..i]);
                let filename = str::from_utf8(v.as_slice());
                println!("file requested: {}", filename.unwrap());
                let mut writer = FileStreamWriter::new(String::from(filename.unwrap())).unwrap();
                //send ACK
                let mut block_num = 0u16;
                loop{
                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    println!("Sending ACK");
                    socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    Self::clear_buf(&mut buf);
                    let (amt, src) = try!(socket.recv_from(&mut buf));

                    writer.append(&mut buf);
                    if amt - 4 < 512{
                        //TODO do we close writer here?
                        break;
                    }
                    block_num += 1;

                }

            },
            4 => {}
            _ => panic!("unrecognized packet type"),
        }

        Ok(())

    }

    fn clear_buf(buf : &mut[u8]){
        assert!(buf.len() == 516);
        for i in 0..516{
            buf[i] = 0;
        }
    }

    fn create_data_packet(&mut self, blk_id: u16, buf: &[u8], num_bytes: usize) -> Vec<u8> {
        let mut v = Vec::with_capacity(num_bytes + 4);
        v.push(0);
        v.push(PacketType::DATA as u8);
        v.push((blk_id >> 8) as u8);
        v.push((blk_id & 0x00FF) as u8);
        v.extend_from_slice(&buf[0..num_bytes]);
        v
    }

    fn create_rrq_wrq_packet(&self,
                             p_type: PacketType,
                             file_name: &str,
                             mode: &'static str)
                             -> Vec<u8> {
        let mut v = Vec::with_capacity(2 + file_name.len() + 1 + mode.len() + 1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push('\0' as u8);
        v.push(0);//zero terminator
        v.extend(mode.as_bytes());
        v.push(0);//zero terminator
        v
    }

    pub fn get(&mut self, files: &[&str], mode: &'static str) -> Result<(), Error> {
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf = [0; 516];
        for file in files {
            // start with a RRQ
            println!("sending RRQ request");
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection.unwrap()));
            // TODO remove the hardcoded filename
            let path = Path::new(file);

            println!("Downloading as ./{}", path.file_name().unwrap().to_str().unwrap());
            //TODO fix downloading very file in the current dir
            let mut writer =
                FileStreamWriter::new(format!("./{}", path.file_name().unwrap().to_str().unwrap()))
                    .unwrap();

            loop {
                // get the first block of the requested file
                let (amt, src) = try!(socket.recv_from(&mut buf));
                println!("Received block");
                println!("Total bytes read {}", amt);

                match buf[1] {
                    3 => {
                        let block_size = amt - 4;
                        println!("File block size is {:?}", block_size);
                        if block_size == 0 {
                            println!("Empty block recvd");
                            writer.close();
                            break;
                        }
                        println!("Appending to file");
                        writer.append(&buf[4..4 + block_size]);
                        let block_num: u16 = 0u16 | (buf[2] as u16) << 8 | buf[3] as u16;

                        // send ACK
                        let low = block_num & 0x00FF;
                        let high = (block_num & 0xFF00) >> 8;

                        println!("Sending ACK");
                        socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    }
                    _ => {}
                }
                // Flush buf for next read
                // TODO fix this:
                for i in 0..516 {
                    buf[0] = 0;
                }
            }

        }

        Ok(())
    }
}

fn get_ip_from(ip: &str) -> Result<Ipv4Addr, AddrParseError> {
    Ipv4Addr::from_str(ip)
}
<commit_msg>Add a TODO<commit_after>
extern crate rand;
// extern crate time;

// use time::PreciseTime;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::{FileStreamReader, FileStreamWriter};
use std::io::Error;
use tftp_specific::PacketType;
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;
use std::str;
use std::path::Path;

pub struct EndPoint {
    local_connection: SocketAddrV4,
    // A server doesn't need a remote_connection
    remote_connection: Option<SocketAddrV4>,
}

impl EndPoint {
    pub fn new(remote: &str, port: &str, is_server: bool) -> Result<EndPoint, AddrParseError> {

        let ipv4 = try!(get_ip_from(remote));
        if is_server {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 6900),
                remote_connection: None,
            })
        } else {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9990),
                remote_connection: Some(SocketAddrV4::new(ipv4, 6900)),
            })
        }
    }

    pub fn start_listen(&mut self) -> Result<(), Error> {
        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Start servicing requests from a client
        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        let mut last_blk_id = 0u16;
        match buf[1] {
            1 => {
                // RRQ
                println!("RRQ received");
                // Get filename to be sent from the packet
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }
                let filename = str::from_utf8(&buf[2..i]);
                println!("file requested: {}", filename.unwrap());
                // Send data via the socket
                let mut fs = FileStreamReader::new(String::from(filename.unwrap())).unwrap();
                let (mut buf, num_bytes_read) = fs.next().unwrap();
                let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                try!(socket.send_to(v.as_slice(), src));
                // TODO start timer here
                loop {
                    // Get ACK
                    let (amt, src) = try!(socket.recv_from(&mut buf));
                    if buf[1] == 4 {
                        println!("ACK received");
                        last_blk_id += 1;
                        let (buf, num_bytes_read) = fs.next().unwrap();
                        let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                        try!(socket.send_to(v.as_slice(), src));
                    }
                }
            },
            2 => {
                //WRQ
                println!("WRQ received");
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }

                //TODO fix the vec use to copy slice from buf
                let mut v = vec![];
                v.copy_from_slice(&buf[2..i]);
                let filename = str::from_utf8(v.as_slice());
                println!("file requested: {}", filename.unwrap());
                let mut writer = FileStreamWriter::new(String::from(filename.unwrap())).unwrap();
                //send ACK
                let mut block_num = 0u16;
                loop{
                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    println!("Sending ACK");
                    socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    Self::clear_buf(&mut buf);
                    let (amt, src) = try!(socket.recv_from(&mut buf));

                    writer.append(&mut buf);
                    if amt - 4 < 512{
                        //TODO do we close writer here?
                        break;
                    }
                    block_num += 1;

                }

            },
            4 => {}
            _ => panic!("unrecognized packet type"),
        }

        Ok(())

    }

    fn clear_buf(buf : &mut[u8]){
        assert!(buf.len() == 516);
        for i in 0..516{
            buf[i] = 0;
        }
    }

    fn create_data_packet(&mut self, blk_id: u16, buf: &[u8], num_bytes: usize) -> Vec<u8> {
        let mut v = Vec::with_capacity(num_bytes + 4);
        v.push(0);
        v.push(PacketType::DATA as u8);
        v.push((blk_id >> 8) as u8);
        v.push((blk_id & 0x00FF) as u8);
        v.extend_from_slice(&buf[0..num_bytes]);
        v
    }

    fn create_rrq_wrq_packet(&self,
                             p_type: PacketType,
                             file_name: &str,
                             mode: &'static str)
                             -> Vec<u8> {
        let mut v = Vec::with_capacity(2 + file_name.len() + 1 + mode.len() + 1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push('\0' as u8);
        v.push(0);//zero terminator
        v.extend(mode.as_bytes());
        v.push(0);//zero terminator
        v
    }

    pub fn get(&mut self, files: &[&str], mode: &'static str) -> Result<(), Error> {
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf = [0; 516];
        for file in files {
            // start with a RRQ
            println!("sending RRQ request");
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection.unwrap()));
            // TODO remove the hardcoded filename
            let path = Path::new(file);

            println!("Downloading as ./{}", path.file_name().unwrap().to_str().unwrap());
            //TODO fix downloading very file in the current dir
            let mut writer =
                FileStreamWriter::new(format!("./{}", path.file_name().unwrap().to_str().unwrap()))
                    .unwrap();

            loop {
                // get the first block of the requested file
                let (amt, src) = try!(socket.recv_from(&mut buf));
                println!("Received block");
                println!("Total bytes read {}", amt);

                match buf[1] {
                    3 => {
                        let block_size = amt - 4;
                        println!("File block size is {:?}", block_size);
                        if block_size == 0 {
                            println!("Empty block recvd");
                            writer.close();
                            break;
                        }
                        println!("Appending to file");
                        writer.append(&buf[4..4 + block_size]);
                        let block_num: u16 = 0u16 | (buf[2] as u16) << 8 | buf[3] as u16;

                        // send ACK
                        let low = block_num & 0x00FF;
                        let high = (block_num & 0xFF00) >> 8;

                        println!("Sending ACK");
                        socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    }
                    _ => {}
                }
                // Flush buf for next read
                // TODO fix this:
                for i in 0..516 {
                    buf[0] = 0;
                }
            }

        }

        Ok(())
    }
}

fn get_ip_from(ip: &str) -> Result<Ipv4Addr, AddrParseError> {
    Ipv4Addr::from_str(ip)
}
<|endoftext|>


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

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 * (c) Jian Zeng <anonymousknight86@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: cat (GNU coreutils) 8.13 */

extern crate getopts;
extern crate time;

#[macro_use]
extern crate uucore;
// import crate time from utmpx
use uucore::libc::time_t;
pub use uucore::libc;

use getopts::Options;

static NAME: &str = "uptime";
static VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg(unix)]
use libc::getloadavg;

#[cfg(windows)]
extern "C" {
    fn GetTickCount() -> libc::uint32_t;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("v", "version", "output version information and exit");
    opts.optflag("h", "help", "display this help and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f),
    };
    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }
    if matches.opt_present("help") || !matches.free.is_empty() {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]", NAME);
        println!("");
        println!(
            "{}",
            opts.usage(
                "Print the current time, the length of time the system has been up,\n\
                 the number of users on the system, and the average number of jobs\n\
                 in the run queue over the last 1, 5 and 15 minutes."
            )
        );
        return 0;
    }

    print_time();
    let (boot_time, user_count) = process_utmpx();
    let uptime = get_uptime(boot_time);
    if uptime < 0 {
        show_error!("could not retrieve system uptime");

        1
    } else {
        let upsecs = uptime / 100;
        print_uptime(upsecs);
        print_nusers(user_count);
        print_loadavg();

        0
    }
}

#[cfg(unix)]
fn print_loadavg() {
    use libc::c_double;
    use std::mem::transmute;

    let mut avg: [c_double; 3] = [0.0; 3];
    let loads: i32 = unsafe { transmute(getloadavg(avg.as_mut_ptr(), 3)) };

    if loads == -1 {
        print!("\n");
    } else {
        print!("load average: ");
        for n in 0..loads {
            print!(
                "{:.2}{}",
                avg[n as usize],
                if n == loads - 1 { "\n" } else { ", " }
            );
        }
    }
}

#[cfg(windows)]
fn print_loadavg() {
    // XXX: currently this is a noop as Windows does not seem to have anything comparable to
    //      getloadavg()
}

#[cfg(unix)]
fn process_utmpx() -> (Option<time_t>, usize) {
    use uucore::utmpx::*;

    let mut nusers = 0;
    let mut boot_time = None;

    for line in Utmpx::iter_all_records() {
        match line.record_type() {
            USER_PROCESS => nusers += 1,
            BOOT_TIME => {
                let t = line.login_time().to_timespec();
                if t.sec > 0 {
                    boot_time = Some(t.sec as time_t);
                }
            }
            _ => continue,
        }
    }
    (boot_time, nusers)
}

#[cfg(windows)]
fn process_utmpx() -> (Option<time_t>, usize) {
    (None, 0) // TODO: change 0 to number of users
}

fn print_nusers(nusers: usize) {
    if nusers == 1 {
        print!("1 user, ");
    } else if nusers > 1 {
        print!("{} users, ", nusers);
    }
}

fn print_time() {
    let local_time = time::now();

    print!(
        " {:02}:{:02}:{:02} ",
        local_time.tm_hour, local_time.tm_min, local_time.tm_sec
    );
}

#[cfg(unix)]
fn get_uptime(boot_time: Option<time_t>) -> i64 {
    use std::fs::File;
    use std::io::Read;

    let mut proc_uptime = String::new();

    if let Some(n) = File::open("/proc/uptime")
        .ok()
        .and_then(|mut f| f.read_to_string(&mut proc_uptime).ok())
        .and_then(|_| proc_uptime.split_whitespace().next())
        .and_then(|s| s.replace(".", "").parse().ok())
    {
        n
    } else {
        match boot_time {
            Some(t) => {
                let now = time::get_time().sec;
                let boottime = t as i64;
                ((now - boottime) * 100)
            }
            _ => -1,
        }
    }
}

#[cfg(windows)]
fn get_uptime(_boot_time: Option<time_t>) -> i64 {
    unsafe { GetTickCount() as i64 }
}

fn print_uptime(upsecs: i64) {
    let updays = upsecs / 86400;
    let uphours = (upsecs - (updays * 86400)) / 3600;
    let upmins = (upsecs - (updays * 86400) - (uphours * 3600)) / 60;
    if updays == 1 {
        print!("up {:1} day, {:2}:{:02}, ", updays, uphours, upmins);
    } else if updays > 1 {
        print!("up {:1} days, {:2}:{:02}, ", updays, uphours, upmins);
    } else {
        print!("up  {:2}:{:02}, ", uphours, upmins);
    }
}
<commit_msg>uptime: match the gnu version output<commit_after>#![crate_name = "uu_uptime"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 * (c) Jian Zeng <anonymousknight86@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: cat (GNU coreutils) 8.13 */

extern crate getopts;
extern crate time;

#[macro_use]
extern crate uucore;
// import crate time from utmpx
use uucore::libc::time_t;
pub use uucore::libc;

use getopts::Options;

static NAME: &str = "uptime";
static VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg(unix)]
use libc::getloadavg;

#[cfg(windows)]
extern "C" {
    fn GetTickCount() -> libc::uint32_t;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = Options::new();

    opts.optflag("v", "version", "output version information and exit");
    opts.optflag("h", "help", "display this help and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f),
    };
    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }
    if matches.opt_present("help") || !matches.free.is_empty() {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]", NAME);
        println!("");
        println!(
            "{}",
            opts.usage(
                "Print the current time, the length of time the system has been up,\n\
                 the number of users on the system, and the average number of jobs\n\
                 in the run queue over the last 1, 5 and 15 minutes."
            )
        );
        return 0;
    }

    print_time();
    let (boot_time, user_count) = process_utmpx();
    let uptime = get_uptime(boot_time);
    if uptime < 0 {
        show_error!("could not retrieve system uptime");

        1
    } else {
        let upsecs = uptime / 100;
        print_uptime(upsecs);
        print_nusers(user_count);
        print_loadavg();

        0
    }
}

#[cfg(unix)]
fn print_loadavg() {
    use libc::c_double;
    use std::mem::transmute;

    let mut avg: [c_double; 3] = [0.0; 3];
    let loads: i32 = unsafe { transmute(getloadavg(avg.as_mut_ptr(), 3)) };

    if loads == -1 {
        print!("\n");
    } else {
        print!("load average: ");
        for n in 0..loads {
            print!(
                "{:.2}{}",
                avg[n as usize],
                if n == loads - 1 { "\n" } else { ", " }
            );
        }
    }
}

#[cfg(windows)]
fn print_loadavg() {
    // XXX: currently this is a noop as Windows does not seem to have anything comparable to
    //      getloadavg()
}

#[cfg(unix)]
fn process_utmpx() -> (Option<time_t>, usize) {
    use uucore::utmpx::*;

    let mut nusers = 0;
    let mut boot_time = None;

    for line in Utmpx::iter_all_records() {
        match line.record_type() {
            USER_PROCESS => nusers += 1,
            BOOT_TIME => {
                let t = line.login_time().to_timespec();
                if t.sec > 0 {
                    boot_time = Some(t.sec as time_t);
                }
            }
            _ => continue,
        }
    }
    (boot_time, nusers)
}

#[cfg(windows)]
fn process_utmpx() -> (Option<time_t>, usize) {
    (None, 0) // TODO: change 0 to number of users
}

fn print_nusers(nusers: usize) {
    if nusers == 1 {
        print!("1 user,  ");
    } else if nusers > 1 {
        print!("{} users,  ", nusers);
    }
}

fn print_time() {
    let local_time = time::now();

    print!(
        " {:02}:{:02}:{:02} ",
        local_time.tm_hour, local_time.tm_min, local_time.tm_sec
    );
}

#[cfg(unix)]
fn get_uptime(boot_time: Option<time_t>) -> i64 {
    use std::fs::File;
    use std::io::Read;

    let mut proc_uptime = String::new();

    if let Some(n) = File::open("/proc/uptime")
        .ok()
        .and_then(|mut f| f.read_to_string(&mut proc_uptime).ok())
        .and_then(|_| proc_uptime.split_whitespace().next())
        .and_then(|s| s.replace(".", "").parse().ok())
    {
        n
    } else {
        match boot_time {
            Some(t) => {
                let now = time::get_time().sec;
                let boottime = t as i64;
                ((now - boottime) * 100)
            }
            _ => -1,
        }
    }
}

#[cfg(windows)]
fn get_uptime(_boot_time: Option<time_t>) -> i64 {
    unsafe { GetTickCount() as i64 }
}

fn print_uptime(upsecs: i64) {
    let updays = upsecs / 86400;
    let uphours = (upsecs - (updays * 86400)) / 3600;
    let upmins = (upsecs - (updays * 86400) - (uphours * 3600)) / 60;
    if updays == 1 {
        print!("up {:1} day, {:2}:{:02},  ", updays, uphours, upmins);
    } else if updays > 1 {
        print!("up {:1} days, {:2}:{:02},  ", updays, uphours, upmins);
    } else {
        print!("up  {:2}:{:02}, ", uphours, upmins);
    }
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate chrono;

use command_executor::{Command, CommandContext, CommandMetadata, CommandParams, CommandGroup, CommandGroupMetadata};
use commands::*;

use libindy::ErrorCode;
use libindy::payment::Payment;

use serde_json::Value as JSONValue;
use serde_json::Map as JSONMap;

use utils::table::print_list_table;

pub mod group {
    use super::*;

    command_group!(CommandGroupMetadata::new("payment-address", "Payment address management commands"));
}

pub mod create_command {
    use super::*;

    command!(CommandMetadata::build("create", "Create the payment address for specified payment method.")
                .add_required_param("payment_method", "Payment method to use")
                .add_optional_param("seed", "Seed for creating payment address")
                .add_example("payment-address create payment_method=sov")
                .add_example("payment-address create payment_method=sov seed=000000000000000000000000000Seed1")
                .finalize()
    );

    fn execute(ctx: &CommandContext, params: &CommandParams) -> Result<(), ()> {
        trace!("execute >> ctx {:?} params {:?}", ctx, params);

        let wallet_handle = ensure_opened_wallet_handle(&ctx)?;

        let payment_method = get_str_param("payment_method", params).map_err(error_err!())?;
        let seed = get_opt_str_param("seed", params).map_err(error_err!())?;

        let config = {
            let mut json = JSONMap::new();
            update_json_map_opt_key!(json, "seed", seed);
            JSONValue::from(json).to_string()
        };

        let res = match Payment::create_payment_address(wallet_handle, payment_method, &config) {
            Ok(payment_address) => Ok(println_succ!("Payment Address has been created \"{}\"", payment_address)),
            Err(err) => Err(handle_payment_error(err, Some(payment_method))),
        };

        trace!("execute << {:?}", res);
        res
    }
}

pub mod list_command {
    use super::*;

    command!(CommandMetadata::build("list", "Lists all payment addresses that are stored in the wallet.")
                .finalize());

    fn execute(ctx: &CommandContext, params: &CommandParams) -> Result<(), ()> {
        trace!("execute >> ctx {:?} params {:?}", ctx, params);

        let wallet_handle = ensure_opened_wallet_handle(&ctx)?;

        let res = match Payment::list_payment_addresses(wallet_handle) {
            Ok(payment_addresses_json) => {
                let mut payment_addresses: Vec<String> = serde_json::from_str(&payment_addresses_json)
                    .map_err(|_| println_err!("Wrong data has been received"))?;

                let list_addresses =
                    payment_addresses.iter()
                        .map(|payment_address| {
                            let parts = payment_address.split(":").collect::<Vec<&str>>();
                            json!({
                                "address": payment_address,
                                "method": parts.get(1).unwrap_or(&"Unknown payment method")
                            })
                        })
                        .collect::<Vec<serde_json::Value>>();

                print_list_table(&list_addresses,
                                 &vec![("address", "Payment Address"),
                                       ("method", "Payment Method")],
                                 "There are no payment addresses");
                Ok(())
            }
            Err(err) => Err(println_err!("Indy SDK error occurred {:?}", err)),
        };

        trace!("execute << {:?}", res);
        res
    }
}

pub fn handle_payment_error(err: ErrorCode, payment_method: Option<&str>) {
    match err {
        ErrorCode::PaymentUnknownMethodError => println_err!("Unknown payment method {}", payment_method.unwrap_or("")),
        ErrorCode::PaymentIncompatibleMethodsError => println_err!("No methods were scraped or more than one was scraped"),
        ErrorCode::PaymentInsufficientFundsError => println_err!("Insufficient funds on inputs"),
        ErrorCode::CommonInvalidState => println_err!("Input not found"),
        ErrorCode::PaymentSourceDoesNotExistError => println_err!("Payment source not found"),
        ErrorCode::PaymentOperationNotSupportedError => println_err!("Payment operation not supported"),
        ErrorCode::CommonInvalidStructure => println_err!("Invalid format of command params. Please check format of posted JSONs, Keys, DIDs and etc..."),
        err => println_err!("Indy SDK error occurred {:?}", err)
    }
}

#[cfg(test)]
#[cfg(feature = "nullpay_plugin")]
pub mod tests {
    use super::*;
    use utils::test::TestUtils;
    use commands::common::tests::{load_null_payment_plugin, NULL_PAYMENT_METHOD};
    use commands::wallet::tests::{create_and_open_wallet, close_and_delete_wallet};
    use commands::did::tests::SEED_MY1;

    mod create {
        use super::*;

        #[test]
        pub fn create_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());
            assert!(addresses[0].starts_with("pay:null:"));

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_seed() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                params.insert("seed", SEED_MY1.to_string());
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());
//            assert_eq!("pay:null:AkQr7K6CP1tslXd", addresses[0]);  TODO: Exactly check

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_unknown_payment_method() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", "unknown_payment_method".to_string());
                cmd.execute(&ctx, &params).unwrap_err();
            }
            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_no_opened_wallet() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                cmd.execute(&ctx, &params).unwrap_err();
            }
            TestUtils::cleanup_storage();
        }
    }

    mod list {
        use super::*;

        #[test]
        pub fn list_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            create_payment_address(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn list_works_for_empty_list() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(0, addresses.len());

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn list_works_for_no_opened_wallet() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            load_null_payment_plugin(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap_err();
            }
            TestUtils::cleanup_storage();
        }
    }

    fn list_payment_addresses(ctx: &CommandContext) -> Vec<String> {
        let wallet_handle = ensure_opened_wallet_handle(ctx).unwrap();
        let payment_addresses = Payment::list_payment_addresses(wallet_handle).unwrap();
        serde_json::from_str(&payment_addresses).unwrap()
    }

    pub fn create_payment_address(ctx: &CommandContext) -> String {
        let wallet_handle = ensure_opened_wallet_handle(ctx).unwrap();
        Payment::create_payment_address(wallet_handle, NULL_PAYMENT_METHOD, "{}").unwrap()
    }
}
<commit_msg>Add new error message for payment commands in cli<commit_after>extern crate regex;
extern crate chrono;

use command_executor::{Command, CommandContext, CommandMetadata, CommandParams, CommandGroup, CommandGroupMetadata};
use commands::*;

use libindy::ErrorCode;
use libindy::payment::Payment;

use serde_json::Value as JSONValue;
use serde_json::Map as JSONMap;

use utils::table::print_list_table;

pub mod group {
    use super::*;

    command_group!(CommandGroupMetadata::new("payment-address", "Payment address management commands"));
}

pub mod create_command {
    use super::*;

    command!(CommandMetadata::build("create", "Create the payment address for specified payment method.")
                .add_required_param("payment_method", "Payment method to use")
                .add_optional_param("seed", "Seed for creating payment address")
                .add_example("payment-address create payment_method=sov")
                .add_example("payment-address create payment_method=sov seed=000000000000000000000000000Seed1")
                .finalize()
    );

    fn execute(ctx: &CommandContext, params: &CommandParams) -> Result<(), ()> {
        trace!("execute >> ctx {:?} params {:?}", ctx, params);

        let wallet_handle = ensure_opened_wallet_handle(&ctx)?;

        let payment_method = get_str_param("payment_method", params).map_err(error_err!())?;
        let seed = get_opt_str_param("seed", params).map_err(error_err!())?;

        let config = {
            let mut json = JSONMap::new();
            update_json_map_opt_key!(json, "seed", seed);
            JSONValue::from(json).to_string()
        };

        let res = match Payment::create_payment_address(wallet_handle, payment_method, &config) {
            Ok(payment_address) => Ok(println_succ!("Payment Address has been created \"{}\"", payment_address)),
            Err(err) => Err(handle_payment_error(err, Some(payment_method))),
        };

        trace!("execute << {:?}", res);
        res
    }
}

pub mod list_command {
    use super::*;

    command!(CommandMetadata::build("list", "Lists all payment addresses that are stored in the wallet.")
                .finalize());

    fn execute(ctx: &CommandContext, params: &CommandParams) -> Result<(), ()> {
        trace!("execute >> ctx {:?} params {:?}", ctx, params);

        let wallet_handle = ensure_opened_wallet_handle(&ctx)?;

        let res = match Payment::list_payment_addresses(wallet_handle) {
            Ok(payment_addresses_json) => {
                let mut payment_addresses: Vec<String> = serde_json::from_str(&payment_addresses_json)
                    .map_err(|_| println_err!("Wrong data has been received"))?;

                let list_addresses =
                    payment_addresses.iter()
                        .map(|payment_address| {
                            let parts = payment_address.split(":").collect::<Vec<&str>>();
                            json!({
                                "address": payment_address,
                                "method": parts.get(1).unwrap_or(&"Unknown payment method")
                            })
                        })
                        .collect::<Vec<serde_json::Value>>();

                print_list_table(&list_addresses,
                                 &vec![("address", "Payment Address"),
                                       ("method", "Payment Method")],
                                 "There are no payment addresses");
                Ok(())
            }
            Err(err) => Err(println_err!("Indy SDK error occurred {:?}", err)),
        };

        trace!("execute << {:?}", res);
        res
    }
}

pub fn handle_payment_error(err: ErrorCode, payment_method: Option<&str>) {
    match err {
        ErrorCode::PaymentUnknownMethodError => println_err!("Unknown payment method {}", payment_method.unwrap_or("")),
        ErrorCode::PaymentIncompatibleMethodsError => println_err!("No methods were scraped or more than one was scraped"),
        ErrorCode::PaymentInsufficientFundsError => println_err!("Insufficient funds on inputs"),
        ErrorCode::CommonInvalidState => println_err!("Input not found"),
        ErrorCode::PaymentSourceDoesNotExistError => println_err!("Payment source not found"),
        ErrorCode::PaymentOperationNotSupportedError => println_err!("Payment operation not supported"),
        ErrorCode::CommonInvalidStructure => println_err!("Invalid format of command params. Please check format of posted JSONs, Keys, DIDs and etc..."),
        ErrorCode::WalletItemAlreadyExists => println_err!("Payment address already exists"),
        err => println_err!("Indy SDK error occurred {:?}", err)
    }
}

#[cfg(test)]
#[cfg(feature = "nullpay_plugin")]
pub mod tests {
    use super::*;
    use utils::test::TestUtils;
    use commands::common::tests::{load_null_payment_plugin, NULL_PAYMENT_METHOD};
    use commands::wallet::tests::{create_and_open_wallet, close_and_delete_wallet};
    use commands::did::tests::SEED_MY1;

    mod create {
        use super::*;

        #[test]
        pub fn create_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());
            assert!(addresses[0].starts_with("pay:null:"));

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_seed() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                params.insert("seed", SEED_MY1.to_string());
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());
//            assert_eq!("pay:null:AkQr7K6CP1tslXd", addresses[0]);  TODO: Exactly check

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_unknown_payment_method() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", "unknown_payment_method".to_string());
                cmd.execute(&ctx, &params).unwrap_err();
            }
            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn create_works_for_no_opened_wallet() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();
            load_null_payment_plugin(&ctx);
            {
                let cmd = create_command::new();
                let mut params = CommandParams::new();
                params.insert("payment_method", NULL_PAYMENT_METHOD.to_string());
                cmd.execute(&ctx, &params).unwrap_err();
            }
            TestUtils::cleanup_storage();
        }
    }

    mod list {
        use super::*;

        #[test]
        pub fn list_works() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            create_payment_address(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(1, addresses.len());

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn list_works_for_empty_list() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            create_and_open_wallet(&ctx);
            load_null_payment_plugin(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap();
            }
            let addresses = list_payment_addresses(&ctx);
            assert_eq!(0, addresses.len());

            close_and_delete_wallet(&ctx);
            TestUtils::cleanup_storage();
        }

        #[test]
        pub fn list_works_for_no_opened_wallet() {
            TestUtils::cleanup_storage();
            let ctx = CommandContext::new();

            load_null_payment_plugin(&ctx);
            {
                let cmd = list_command::new();
                let params = CommandParams::new();
                cmd.execute(&ctx, &params).unwrap_err();
            }
            TestUtils::cleanup_storage();
        }
    }

    fn list_payment_addresses(ctx: &CommandContext) -> Vec<String> {
        let wallet_handle = ensure_opened_wallet_handle(ctx).unwrap();
        let payment_addresses = Payment::list_payment_addresses(wallet_handle).unwrap();
        serde_json::from_str(&payment_addresses).unwrap()
    }

    pub fn create_payment_address(ctx: &CommandContext) -> String {
        let wallet_handle = ensure_opened_wallet_handle(ctx).unwrap();
        Payment::create_payment_address(wallet_handle, NULL_PAYMENT_METHOD, "{}").unwrap()
    }
}
<|endoftext|>