<commit_before>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * No trailing whitespace
//! * No tabs
//! * 80-character lines
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * 4-space tabs
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "dox.rs" |
            "lib.rs" |
            "ctypes.rs" |
            "libc.rs" |
            "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut prev_blank = false;

    for (i, line) in file.lines().enumerate() {
        if line == "" {
            if prev_blank {
                err.error(path, i, "double blank line");
            }
            prev_blank = true;
        } else {
            prev_blank = false;
        }
        if line != line.trim_end() {
            err.error(path, i, "trailing whitespace");
        }
        if line.contains("\t") {
            err.error(path, i, "tab character");
        }
        if line.len() > 80 {
            err.error(path, i, "line longer than 80 chars");
        }
        // This doesn't work any more due to rustfmt changes
        /*if line.contains("#[cfg(") && !line.contains(" if ")
            && !(line.contains("target_endian") ||
                 line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules \
                                    instead of #[cfg]");
            }
        }*/
        if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
            err.error(path, i, "impl ::Copy and ::Clone manually");
        }

        let orig_line = line;
        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub {&line[4..]} else {line};

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") && !orig_line.starts_with(" ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue
        };

        if state as usize > line_state as usize {
            err.error(path, i, &format!("{} found after {} when \
                                         it belongs before",
                                        line_state.desc(), state.desc()));
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{} - {}", path.display(), line + 1, msg);
    }
}
<commit_msg>Only run the cfg check for single lines<commit_after>//! Simple script to verify the coding style of this library
//!
//! ## How to run
//!
//! The first argument to this script is the directory to run on, so running
//! this script should be as simple as:
//!
//! ```notrust
//! rustc ci/style.rs
//! ./style src
//! ```
//!
//! ## Guidelines
//!
//! The current style is:
//!
//! * No trailing whitespace
//! * No tabs
//! * 80-character lines
//! * Specific module layout:
//!     1. use directives
//!     2. typedefs
//!     3. structs
//!     4. constants
//!     5. f! { ... } functions
//!     6. extern functions
//!     7. modules + pub use
//!
//! Things not verified:
//!
//! * alignment
//! * 4-space tabs
//! * leading colons on paths

use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::Path;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let arg = env::args().skip(1).next().unwrap_or(".".to_string());

    let mut errors = Errors { errs: false };
    walk(Path::new(&arg), &mut errors);

    if errors.errs {
        panic!("found some lint errors");
    } else {
        println!("good style!");
    }
}

fn walk(path: &Path, err: &mut Errors) {
    for entry in t!(path.read_dir()).map(|e| t!(e)) {
        let path = entry.path();
        if t!(entry.file_type()).is_dir() {
            walk(&path, err);
            continue
        }

        let name = entry.file_name().into_string().unwrap();
        match &name[..] {
            n if !n.ends_with(".rs") => continue,

            "dox.rs" |
            "lib.rs" |
            "ctypes.rs" |
            "libc.rs" |
            "macros.rs" => continue,

            _ => {}
        }

        let mut contents = String::new();
        t!(t!(fs::File::open(&path)).read_to_string(&mut contents));

        check_style(&contents, &path, err);
    }
}

struct Errors {
    errs: bool,
}

#[derive(Clone, Copy, PartialEq)]
enum State {
    Start,
    Imports,
    Typedefs,
    Structs,
    Constants,
    FunctionDefinitions,
    Functions,
    Modules,
}

fn check_style(file: &str, path: &Path, err: &mut Errors) {
    let mut state = State::Start;
    let mut s_macros = 0;
    let mut f_macros = 0;
    let mut prev_blank = false;

    for (i, line) in file.lines().enumerate() {
        if line == "" {
            if prev_blank {
                err.error(path, i, "double blank line");
            }
            prev_blank = true;
        } else {
            prev_blank = false;
        }
        if line != line.trim_end() {
            err.error(path, i, "trailing whitespace");
        }
        if line.contains("\t") {
            err.error(path, i, "tab character");
        }
        if line.len() > 80 {
            err.error(path, i, "line longer than 80 chars");
        }
        if line.contains("#[cfg(") && line.contains(']') && !line.contains(" if ")
            && !(line.contains("target_endian") ||
                 line.contains("target_arch"))
        {
            if state != State::Structs {
                err.error(path, i, "use cfg_if! and submodules \
                                    instead of #[cfg]");
            }
        }
        if line.contains("#[derive(") && (line.contains("Copy") || line.contains("Clone")) {
            err.error(path, i, "impl ::Copy and ::Clone manually");
        }

        let orig_line = line;
        let line = line.trim_start();
        let is_pub = line.starts_with("pub ");
        let line = if is_pub {&line[4..]} else {line};

        let line_state = if line.starts_with("use ") {
            if line.contains("c_void") {
                continue;
            }
            if is_pub {
                State::Modules
            } else {
                State::Imports
            }
        } else if line.starts_with("const ") {
            State::Constants
        } else if line.starts_with("type ") {
            State::Typedefs
        } else if line.starts_with("s! {") {
            s_macros += 1;
            State::Structs
        } else if line.starts_with("f! {") {
            f_macros += 1;
            State::FunctionDefinitions
        } else if line.starts_with("extern ") && !orig_line.starts_with(" ") {
            State::Functions
        } else if line.starts_with("mod ") {
            State::Modules
        } else {
            continue
        };

        if state as usize > line_state as usize {
            err.error(path, i, &format!("{} found after {} when \
                                         it belongs before",
                                        line_state.desc(), state.desc()));
        }

        if f_macros == 2 {
            f_macros += 1;
            err.error(path, i, "multiple f! macros in one module");
        }
        if s_macros == 2 {
            s_macros += 1;
            err.error(path, i, "multiple s! macros in one module");
        }

        state = line_state;
    }
}

impl State {
    fn desc(&self) -> &str {
        match *self {
            State::Start => "start",
            State::Imports => "import",
            State::Typedefs => "typedef",
            State::Structs => "struct",
            State::Constants => "constant",
            State::FunctionDefinitions => "function definition",
            State::Functions => "extern function",
            State::Modules => "module",
        }
    }
}

impl Errors {
    fn error(&mut self, path: &Path, line: usize, msg: &str) {
        self.errs = true;
        println!("{}:{} - {}", path.display(), line + 1, msg);
    }
}
<|endoftext|>


<commit_before>extern crate heck;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate syntex_syntax;

mod c;
mod csharp;
mod emscripten;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;

use structopt::StructOpt;
use std::path::Path;
use syntex_syntax::abi::Abi;
use syntex_syntax::ast::{FunctionRetTy, ItemKind, Mutability, PatKind, TyKind, Visibility};
use syntex_syntax::parse::{parse_crate_from_file, ParseSess};
use syntex_syntax::codemap::FilePathMapping;

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(long = "ruby-lib-path", help = "The path of the library for the Ruby bindings",
                default_value = "./liblivesplit_core.so")]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some(&(ref name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &TyKind) -> Type {
    if let &TyKind::Ptr(ref ptr) = ty {
        let mut ty = get_type(&ptr.ty.node);
        ty.kind = if ptr.mutbl == Mutability::Mutable {
            TypeKind::RefMut
        } else {
            TypeKind::Ref
        };
        return ty;
    } else if let &TyKind::Path(_, ref path) = ty {
        if let Some(segment) = path.segments.last() {
            let mut name = segment.identifier.name.to_string();
            let is_nullable = if name.starts_with("Nullable") {
                name = name["Nullable".len()..].to_string();
                true
            } else {
                false
            };

            if name.starts_with("Owned") {
                name = name["Owned".len()..].to_string();
            }
            if name == "TimingMethod" {
                name = String::from("u8");
            } else if name == "TimerPhase" {
                name = String::from("u8");
            }
            let is_custom = match &name as &str {
                "u8" |
                "u16" |
                "u32" |
                "u64" |
                "i8" |
                "i16" |
                "i32" |
                "i64" |
                "()" |
                "bool" |
                "c_char" |
                "usize" |
                "isize" |
                "f32" |
                "f64" |
                "Json" => false,
                _ => true,
            };
            return Type {
                kind: TypeKind::Value,
                is_custom,
                is_nullable,
                name,
            };
        }
    }
    panic!("Unknown type {:#?}", ty);
}

fn main() {
    let opt = Opt::from_args();

    let sess = ParseSess::new(FilePathMapping::empty());
    let ast = parse_crate_from_file(Path::new("../src/lib.rs"), &sess).unwrap();
    let items = ast.module.items;

    let mut functions = Vec::new();

    for item in &items {
        if let &ItemKind::Mod(ref module) = &item.node {
            for item in &module.items {
                if let &ItemKind::Fn(ref decl, _, _, Abi::C, _, _) = &item.node {
                    if item.vis == Visibility::Public &&
                        item.attrs.iter().any(|a| a.check_name("no_mangle"))
                    {
                        let output = if let &FunctionRetTy::Ty(ref output) = &decl.output {
                            get_type(&output.node)
                        } else {
                            Type {
                                kind: TypeKind::Value,
                                is_custom: false,
                                is_nullable: false,
                                name: String::from("()"),
                            }
                        };

                        let inputs = decl.inputs
                            .iter()
                            .map(|i| {
                                let name = if let &PatKind::Ident(_, ref ident, _) = &i.pat.node {
                                    ident.node.name.to_string()
                                } else {
                                    String::from("parameter")
                                };
                                (name, get_type(&i.ty.node))
                            })
                            .collect();

                        let name = item.ident.name.to_string();
                        let class;
                        let method;
                        {
                            let mut splits = name.splitn(2, '_');
                            class = splits.next().unwrap().to_string();
                            method = splits.next().unwrap().to_string();
                        }

                        functions.push(Function {
                            name: name,
                            class: class,
                            method: method,
                            output: output,
                            inputs: inputs,
                        });
                    }
                }
            }
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

use std::collections::BTreeMap;

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes = BTreeMap::new();

    for function in functions {
        let class = classes
            .entry(function.class.clone())
            .or_insert_with(Class::default);
        let kind = if let Some(&(ref name, ref ty)) = function.inputs.get(0) {
            if name == "this" {
                Some(ty.kind)
            } else {
                None
            }
        } else {
            None
        };
        match kind {
            Some(TypeKind::Value) => class.own_fns.push(function),
            Some(TypeKind::Ref) => class.shared_fns.push(function),
            Some(TypeKind::RefMut) => class.mut_fns.push(function),
            None => class.static_fns.push(function),
        }
    }

    classes
}

use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Result};
use std::path::PathBuf;

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    remove_dir_all(&path).ok();
    create_dir_all(&path)?;

    path.push("emscripten");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<commit_msg>Properly pop the default Ruby Lib Path<commit_after>extern crate heck;
extern crate structopt;
#[macro_use]
extern crate structopt_derive;
extern crate syntex_syntax;

mod c;
mod csharp;
mod emscripten;
mod java;
mod jni_cpp;
mod kotlin;
mod node;
mod python;
mod ruby;
mod swift;
mod typescript;

use structopt::StructOpt;
use std::path::Path;
use syntex_syntax::abi::Abi;
use syntex_syntax::ast::{FunctionRetTy, ItemKind, Mutability, PatKind, TyKind, Visibility};
use syntex_syntax::parse::{parse_crate_from_file, ParseSess};
use syntex_syntax::codemap::FilePathMapping;

#[derive(StructOpt)]
#[structopt(about = "Generates bindings for livesplit-core")]
pub struct Opt {
    #[structopt(long = "ruby-lib-path", help = "The path of the library for the Ruby bindings",
                default_value = "../liblivesplit_core.so")]
    ruby_lib_path: String,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum TypeKind {
    Value,
    Ref,
    RefMut,
}

#[derive(Debug)]
pub struct Type {
    kind: TypeKind,
    is_custom: bool,
    is_nullable: bool,
    name: String,
}

#[derive(Debug)]
pub struct Function {
    name: String,
    class: String,
    method: String,
    inputs: Vec<(String, Type)>,
    output: Type,
}

impl Function {
    fn is_static(&self) -> bool {
        if let Some(&(ref name, _)) = self.inputs.get(0) {
            name != "this"
        } else {
            true
        }
    }

    fn has_return_type(&self) -> bool {
        self.output.name != "()"
    }
}

#[derive(Debug, Default)]
pub struct Class {
    static_fns: Vec<Function>,
    shared_fns: Vec<Function>,
    mut_fns: Vec<Function>,
    own_fns: Vec<Function>,
}

fn get_type(ty: &TyKind) -> Type {
    if let &TyKind::Ptr(ref ptr) = ty {
        let mut ty = get_type(&ptr.ty.node);
        ty.kind = if ptr.mutbl == Mutability::Mutable {
            TypeKind::RefMut
        } else {
            TypeKind::Ref
        };
        return ty;
    } else if let &TyKind::Path(_, ref path) = ty {
        if let Some(segment) = path.segments.last() {
            let mut name = segment.identifier.name.to_string();
            let is_nullable = if name.starts_with("Nullable") {
                name = name["Nullable".len()..].to_string();
                true
            } else {
                false
            };

            if name.starts_with("Owned") {
                name = name["Owned".len()..].to_string();
            }
            if name == "TimingMethod" {
                name = String::from("u8");
            } else if name == "TimerPhase" {
                name = String::from("u8");
            }
            let is_custom = match &name as &str {
                "u8" |
                "u16" |
                "u32" |
                "u64" |
                "i8" |
                "i16" |
                "i32" |
                "i64" |
                "()" |
                "bool" |
                "c_char" |
                "usize" |
                "isize" |
                "f32" |
                "f64" |
                "Json" => false,
                _ => true,
            };
            return Type {
                kind: TypeKind::Value,
                is_custom,
                is_nullable,
                name,
            };
        }
    }
    panic!("Unknown type {:#?}", ty);
}

fn main() {
    let opt = Opt::from_args();

    let sess = ParseSess::new(FilePathMapping::empty());
    let ast = parse_crate_from_file(Path::new("../src/lib.rs"), &sess).unwrap();
    let items = ast.module.items;

    let mut functions = Vec::new();

    for item in &items {
        if let &ItemKind::Mod(ref module) = &item.node {
            for item in &module.items {
                if let &ItemKind::Fn(ref decl, _, _, Abi::C, _, _) = &item.node {
                    if item.vis == Visibility::Public &&
                        item.attrs.iter().any(|a| a.check_name("no_mangle"))
                    {
                        let output = if let &FunctionRetTy::Ty(ref output) = &decl.output {
                            get_type(&output.node)
                        } else {
                            Type {
                                kind: TypeKind::Value,
                                is_custom: false,
                                is_nullable: false,
                                name: String::from("()"),
                            }
                        };

                        let inputs = decl.inputs
                            .iter()
                            .map(|i| {
                                let name = if let &PatKind::Ident(_, ref ident, _) = &i.pat.node {
                                    ident.node.name.to_string()
                                } else {
                                    String::from("parameter")
                                };
                                (name, get_type(&i.ty.node))
                            })
                            .collect();

                        let name = item.ident.name.to_string();
                        let class;
                        let method;
                        {
                            let mut splits = name.splitn(2, '_');
                            class = splits.next().unwrap().to_string();
                            method = splits.next().unwrap().to_string();
                        }

                        functions.push(Function {
                            name: name,
                            class: class,
                            method: method,
                            output: output,
                            inputs: inputs,
                        });
                    }
                }
            }
        }
    }

    write_files(&fns_to_classes(functions), &opt).unwrap();
}

use std::collections::BTreeMap;

fn fns_to_classes(functions: Vec<Function>) -> BTreeMap<String, Class> {
    let mut classes = BTreeMap::new();

    for function in functions {
        let class = classes
            .entry(function.class.clone())
            .or_insert_with(Class::default);
        let kind = if let Some(&(ref name, ref ty)) = function.inputs.get(0) {
            if name == "this" {
                Some(ty.kind)
            } else {
                None
            }
        } else {
            None
        };
        match kind {
            Some(TypeKind::Value) => class.own_fns.push(function),
            Some(TypeKind::Ref) => class.shared_fns.push(function),
            Some(TypeKind::RefMut) => class.mut_fns.push(function),
            None => class.static_fns.push(function),
        }
    }

    classes
}

use std::fs::{create_dir_all, remove_dir_all, File};
use std::io::{BufWriter, Result};
use std::path::PathBuf;

fn write_files(classes: &BTreeMap<String, Class>, opt: &Opt) -> Result<()> {
    let mut path = PathBuf::from("..");
    path.push("bindings");

    remove_dir_all(&path).ok();
    create_dir_all(&path)?;

    path.push("emscripten");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        emscripten::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("node");
    create_dir_all(&path)?;
    {
        path.push("livesplit_core.js");
        node::write(BufWriter::new(File::create(&path)?), classes, false)?;
        path.pop();

        path.push("livesplit_core.ts");
        node::write(BufWriter::new(File::create(&path)?), classes, true)?;
        path.pop();
    }
    path.pop();

    path.push("LiveSplitCore.cs");
    csharp::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("java");
    create_dir_all(&path)?;
    java::write(&path, classes)?;
    path.pop();

    path.push("kotlin");
    create_dir_all(&path)?;
    kotlin::write(&path, classes)?;
    path.pop();

    path.push("LiveSplitCore.rb");
    ruby::write(BufWriter::new(File::create(&path)?), classes, opt)?;
    path.pop();

    path.push("livesplit_core.h");
    c::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("livesplit_core.py");
    python::write(BufWriter::new(File::create(&path)?), classes)?;
    path.pop();

    path.push("swift");
    create_dir_all(&path)?;
    swift::write(&path, classes)?;
    path.pop();

    Ok(())
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::fmt;
use std::hash::Hash;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::enums::Enum;
use crate::reflect::enums::dynamic::DynamicEnumDescriptor;
use crate::reflect::enums::generated::GeneratedEnumDescriptor;
use crate::reflect::file::index::FileIndexEnumEntry;
use crate::reflect::file::FileDescriptorImpl;
use crate::reflect::FileDescriptor;
use crate::reflect::MessageDescriptor;
use crate::EnumFull;

pub(crate) mod dynamic;
pub(crate) mod generated;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumValueDescriptor {
    pub(crate) enum_descriptor: EnumDescriptor,
    pub(crate) index: usize,
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("enum_descriptor", &self.enum_descriptor)
            .field("name", &self.name())
            .finish()
    }
}

impl EnumValueDescriptor {
    pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor,
            index,
        }
    }

    /// `.proto` object which declared this value.
    pub fn proto(&self) -> &EnumValueDescriptorProto {
        &self.enum_descriptor.proto().value[self.index as usize]
    }

    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    /// Fully qualified enum value name: fully qualified enum name followed by value name.
    pub fn full_name(&self) -> String {
        format!("{}.{}", self.enum_descriptor.full_name(), self.name())
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto().number()
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        &self.enum_descriptor
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// ```
    /// # use protobuf::well_known_types::NullValue;
    /// # use protobuf::EnumFull;
    /// # use protobuf::reflect::EnumValueDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: Figure out why.
    /// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
    /// let null: Option<NullValue> = value.cast();
    /// assert_eq!(Some(NullValue::NULL_VALUE), null);
    /// # }
    /// ```
    pub fn cast<E: EnumFull>(&self) -> Option<E> {
        if self.enum_descriptor != E::enum_descriptor() {
            return None;
        }
        E::from_i32(self.value())
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumDescriptor {
    file_descriptor: FileDescriptor,
    index: usize,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            // TODO
            //.field("full_name", &self.full_name)
            .finish_non_exhaustive()
    }
}

impl EnumDescriptor {
    pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get internal index.
    ///
    /// Only needed for codegen.
    #[doc(hidden)]
    pub fn index_in_file_for_codegen(&self) -> usize {
        self.index
    }

    fn get_impl(&self) -> EnumDescriptorImplRef {
        match &self.file_descriptor.imp {
            FileDescriptorImpl::Generated(g) => {
                EnumDescriptorImplRef::Generated(&g.enums[self.index])
            }
            FileDescriptorImpl::Dynamic(d) => EnumDescriptorImplRef::Dynamic(&d.enums[self.index]),
        }
    }

    /// Descriptor objects which defined this enum.
    pub fn proto(&self) -> &EnumDescriptorProto {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.proto,
            EnumDescriptorImplRef::Dynamic(d) => d.proto(),
        }
    }

    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    fn index_entry(&self) -> &FileIndexEnumEntry {
        self.file_descriptor.enum_index_entry(self.index)
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.full_name,
            EnumDescriptorImplRef::Dynamic(d) => &d.full_name,
        }
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: EnumFull>() -> EnumDescriptor {
        E::enum_descriptor()
    }

    #[doc(hidden)]
    pub fn new_generated_2(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get a message containing this message, or `None` if this message is declared at file level.
    pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
        self.index_entry()
            .enclosing_message
            .map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
    }

    /// This enum values
    pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
        let value_len = self.proto().value.len();
        (0..value_len).map(move |index| EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// First variant (also default in proto3).
    pub fn first_value(&self) -> EnumValueDescriptor {
        self.values().next().unwrap()
    }

    /// Find enum variant by name
    pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_name
            .get(name)?;
        Some(EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_number
            .get(&number)?;
        Some(self.value_by_index(index))
    }

    /// Get enum variant by index (as declared in `.proto` file).
    pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        }
    }

    /// Default enum value (first variant)
    pub fn default_value(&self) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index: 0,
        }
    }

    /// Find enum variant by number or return default (first) enum value
    pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => self.default_value(),
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::EnumFull;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: figure out why
    /// let descriptor: EnumDescriptor = Label::enum_descriptor();
    ///
    /// assert!(descriptor.is::<Label>())
    /// }
    /// ```
    pub fn is<E: Enum>(&self) -> bool {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
            EnumDescriptorImplRef::Dynamic(..) => false,
        }
    }
}

enum EnumDescriptorImplRef<'a> {
    Generated(&'static GeneratedEnumDescriptor),
    Dynamic(&'a DynamicEnumDescriptor),
}

#[cfg(test)]
mod test {
    use crate::descriptor::field_descriptor_proto::Type;
    use crate::descriptor::FieldDescriptorProto;
    use crate::well_known_types::NullValue;
    use crate::EnumFull;
    use crate::MessageFull;

    #[test]
    #[cfg_attr(miri, ignore)] // Too slow on Miri.
    fn enclosing_message() {
        assert_eq!(
            Some(FieldDescriptorProto::descriptor_static()),
            Type::enum_descriptor().enclosing_message()
        );
        assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
    }
}
<commit_msg>Better Display for enum descriptors<commit_after>use std::any::TypeId;
use std::fmt;
use std::fmt::Formatter;
use std::hash::Hash;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::enums::Enum;
use crate::reflect::enums::dynamic::DynamicEnumDescriptor;
use crate::reflect::enums::generated::GeneratedEnumDescriptor;
use crate::reflect::file::index::FileIndexEnumEntry;
use crate::reflect::file::FileDescriptorImpl;
use crate::reflect::FileDescriptor;
use crate::reflect::MessageDescriptor;
use crate::EnumFull;

pub(crate) mod dynamic;
pub(crate) mod generated;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumValueDescriptor {
    pub(crate) enum_descriptor: EnumDescriptor,
    pub(crate) index: usize,
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("enum_descriptor", &self.enum_descriptor)
            .field("name", &self.name())
            .finish()
    }
}

impl fmt::Display for EnumValueDescriptor {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.full_name())
    }
}

impl EnumValueDescriptor {
    pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor,
            index,
        }
    }

    /// `.proto` object which declared this value.
    pub fn proto(&self) -> &EnumValueDescriptorProto {
        &self.enum_descriptor.proto().value[self.index as usize]
    }

    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    /// Fully qualified enum value name: fully qualified enum name followed by value name.
    pub fn full_name(&self) -> String {
        format!("{}.{}", self.enum_descriptor.full_name(), self.name())
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto().number()
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        &self.enum_descriptor
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// ```
    /// # use protobuf::well_known_types::NullValue;
    /// # use protobuf::EnumFull;
    /// # use protobuf::reflect::EnumValueDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: Figure out why.
    /// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
    /// let null: Option<NullValue> = value.cast();
    /// assert_eq!(Some(NullValue::NULL_VALUE), null);
    /// # }
    /// ```
    pub fn cast<E: EnumFull>(&self) -> Option<E> {
        if self.enum_descriptor != E::enum_descriptor() {
            return None;
        }
        E::from_i32(self.value())
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumDescriptor {
    file_descriptor: FileDescriptor,
    index: usize,
}

impl fmt::Display for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.full_name())
    }
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            // TODO
            //.field("full_name", &self.full_name)
            .finish_non_exhaustive()
    }
}

impl EnumDescriptor {
    pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get internal index.
    ///
    /// Only needed for codegen.
    #[doc(hidden)]
    pub fn index_in_file_for_codegen(&self) -> usize {
        self.index
    }

    fn get_impl(&self) -> EnumDescriptorImplRef {
        match &self.file_descriptor.imp {
            FileDescriptorImpl::Generated(g) => {
                EnumDescriptorImplRef::Generated(&g.enums[self.index])
            }
            FileDescriptorImpl::Dynamic(d) => EnumDescriptorImplRef::Dynamic(&d.enums[self.index]),
        }
    }

    /// Descriptor objects which defined this enum.
    pub fn proto(&self) -> &EnumDescriptorProto {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.proto,
            EnumDescriptorImplRef::Dynamic(d) => d.proto(),
        }
    }

    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    fn index_entry(&self) -> &FileIndexEnumEntry {
        self.file_descriptor.enum_index_entry(self.index)
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.full_name,
            EnumDescriptorImplRef::Dynamic(d) => &d.full_name,
        }
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: EnumFull>() -> EnumDescriptor {
        E::enum_descriptor()
    }

    #[doc(hidden)]
    pub fn new_generated_2(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get a message containing this message, or `None` if this message is declared at file level.
    pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
        self.index_entry()
            .enclosing_message
            .map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
    }

    /// This enum values
    pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
        let value_len = self.proto().value.len();
        (0..value_len).map(move |index| EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// First variant (also default in proto3).
    pub fn first_value(&self) -> EnumValueDescriptor {
        self.values().next().unwrap()
    }

    /// Find enum variant by name
    pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_name
            .get(name)?;
        Some(EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_number
            .get(&number)?;
        Some(self.value_by_index(index))
    }

    /// Get enum variant by index (as declared in `.proto` file).
    pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        }
    }

    /// Default enum value (first variant)
    pub fn default_value(&self) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index: 0,
        }
    }

    /// Find enum variant by number or return default (first) enum value
    pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => self.default_value(),
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::EnumFull;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: figure out why
    /// let descriptor: EnumDescriptor = Label::enum_descriptor();
    ///
    /// assert!(descriptor.is::<Label>())
    /// }
    /// ```
    pub fn is<E: Enum>(&self) -> bool {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
            EnumDescriptorImplRef::Dynamic(..) => false,
        }
    }
}

enum EnumDescriptorImplRef<'a> {
    Generated(&'static GeneratedEnumDescriptor),
    Dynamic(&'a DynamicEnumDescriptor),
}

#[cfg(test)]
mod test {
    use crate::descriptor::field_descriptor_proto::Label;
    use crate::descriptor::field_descriptor_proto::Type;
    use crate::descriptor::FieldDescriptorProto;
    use crate::well_known_types::NullValue;
    use crate::EnumFull;
    use crate::MessageFull;

    #[test]
    #[cfg_attr(miri, ignore)] // Too slow on Miri.
    fn enclosing_message() {
        assert_eq!(
            Some(FieldDescriptorProto::descriptor_static()),
            Type::enum_descriptor().enclosing_message()
        );
        assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
    }

    #[test]
    fn to_string() {
        assert_eq!(
            "google.protobuf.FieldDescriptorProto.Label",
            Label::enum_descriptor().to_string()
        );
        assert_eq!(
            "google.protobuf.FieldDescriptorProto.Label",
            Label::enum_descriptor().full_name()
        );
        assert_eq!(
            "google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
            Label::LABEL_REPEATED.descriptor().to_string()
        );
        assert_eq!(
            "google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED",
            Label::LABEL_REPEATED.descriptor().full_name()
        );
    }
}
<|endoftext|>


<commit_before>use ::inflector::Inflector;
use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::params;
use infer::query;
use infer::response;
use infer::TokensResult;

const METHODS: &'static [&'static str] = &[
    "DELETE",
    "GET",
    "HEAD",
    "PATCH",
    "POST",
    "PUT",
];

pub(super) fn valid(method: &str) -> bool {
    METHODS.contains(&method.to_uppercase().as_str())
}

fn fn_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

fn mod_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

pub(super) fn infer_v3(method: &str, schema: &Yaml) -> TokensResult {
    let method_fn = fn_ident(&method);
    let method_mod = mod_ident(&method);

    let mut structs: Vec<Tokens> = Vec::new();
    let mut bounds: Vec<Tokens> = Vec::new();
    let mut args: Vec<Tokens> = Vec::new();
    let mut transformations: Vec<Tokens> = Vec::new();

    if let Some(parameters) = schema["parameters"].as_vec() {
        let query_parameters = parameters.iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("query"))
            .collect::<Vec<Yaml>>();

        if !query_parameters.is_empty() {
            let (s, b, a, t) = query::infer_v3(&method_mod, &Yaml::Array(query_parameters))?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        }

        let path_parameters = parameters.iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("path"))
            .collect::<Vec<Yaml>>();

        if !path_parameters.is_empty() {
            let (s, b, a, t) = params::infer_v3(&method_mod, &Yaml::Array(path_parameters))?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        }
    }

    structs.push(response::infer_v3(&schema["responses"])?);

    Ok(quote! {
        pub mod #method_mod {
            #[allow(unused_imports)]
            use super::schema_ref;

            #(#structs)*
        }

        #[allow(dead_code)]
        #[allow(unused_mut)]
        pub fn #method_fn<#(#bounds),*>(#(#args),*) -> #method_mod::ResponseResult {
            let mut url = Url::parse(self::API_URL)
                .expect("Malformed server URL or path.");

            #(#transformations)*

            let client = Client::new().unwrap();
            let request = client.#method_fn(url)
                .header(header::Accept::json());

            let mut response = request.send().ok();
            <#method_mod::ResponseResult as Response>::from(&mut response.as_mut())
        }
    })
}
<commit_msg>Fix setting path when no params<commit_after>use ::inflector::Inflector;
use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::params;
use infer::query;
use infer::response;
use infer::TokensResult;

const METHODS: &'static [&'static str] = &[
    "DELETE",
    "GET",
    "HEAD",
    "PATCH",
    "POST",
    "PUT",
];

pub(super) fn valid(method: &str) -> bool {
    METHODS.contains(&method.to_uppercase().as_str())
}

fn fn_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

fn mod_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

pub(super) fn infer_v3(method: &str, schema: &Yaml) -> TokensResult {
    let method_fn = fn_ident(&method);
    let method_mod = mod_ident(&method);

    let mut structs: Vec<Tokens> = Vec::new();
    let mut bounds: Vec<Tokens> = Vec::new();
    let mut args: Vec<Tokens> = Vec::new();
    let mut transformations: Vec<Tokens> = Vec::new();

    let path_set = quote! {
        url.path_segments_mut().unwrap()
            .extend(self::API_PATH.split('/'));
    };

    if let Some(parameters) = schema["parameters"].as_vec() {
        let query_parameters = parameters.iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("query"))
            .collect::<Vec<Yaml>>();

        let path_parameters = parameters.iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("path"))
            .collect::<Vec<Yaml>>();

        if !query_parameters.is_empty() {
            let (s, b, a, t) = query::infer_v3(&method_mod, &Yaml::Array(query_parameters))?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        }

        if !path_parameters.is_empty() {
            let (s, b, a, t) = params::infer_v3(&method_mod, &Yaml::Array(path_parameters))?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        } else {
            transformations.push(path_set);
        }
    } else {
        transformations.push(path_set);
    }

    structs.push(response::infer_v3(&schema["responses"])?);

    Ok(quote! {
        pub mod #method_mod {
            #[allow(unused_imports)]
            use super::schema_ref;

            #(#structs)*
        }

        #[allow(dead_code)]
        #[allow(unused_mut)]
        pub fn #method_fn<#(#bounds),*>(#(#args),*) -> #method_mod::ResponseResult {
            let mut url = Url::parse(self::API_URL)
                .expect("Malformed server URL or path.");

            #(#transformations)*

            let client = Client::new().unwrap();
            let request = client.#method_fn(url)
                .header(header::Accept::json());

            let mut response = request.send().ok();
            <#method_mod::ResponseResult as Response>::from(&mut response.as_mut())
        }
    })
}
<|endoftext|>


<commit_before>//! A (WIP) C API allowing use of gluon in other langauges than Rust.
#![doc(html_root_url = "https://docs.rs/gluon_c-api/0.7.0")] // # GLUON

extern crate gluon;
#[cfg(feature = "c_void")]
extern crate libc;

use std::str;
use std::slice;

use gluon::vm::api::{CPrimitive, Getable, Hole, OpaqueValue, Pushable};
use gluon::vm::types::{VmIndex, VmInt};
use gluon::vm::thread::{RootedThread, Status, Thread, ThreadInternal};

use gluon::Compiler;

pub type Function = extern "C" fn(&Thread) -> Status;

// TODO What should the c api return as errors
// TODO How should error messages be returned
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum Error {
    Ok,
    Unknown,
}

pub extern "C" fn glu_new_vm() -> *const Thread {
    let vm = RootedThread::new();
    vm.into_raw()
}

pub unsafe extern "C" fn glu_free_vm(vm: &Thread) {
    RootedThread::from_raw(vm);
}

pub unsafe extern "C" fn glu_run_expr(
    vm: &Thread,
    module: &u8,
    module_len: usize,
    expr: &u8,
    expr_len: usize,
) -> Error {
    let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let result = Compiler::new().run_expr::<OpaqueValue<&Thread, Hole>>(&vm, module, expr);
    match result {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

pub unsafe extern "C" fn glu_load_script(
    vm: &Thread,
    module: &u8,
    module_len: usize,
    expr: &u8,
    expr_len: usize,
) -> Error {
    let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let result = Compiler::new().load_script(vm, module, expr);
    match result {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

pub extern "C" fn glu_call_function(thread: &Thread, args: VmIndex) -> Error {
    let context = thread.context();
    match thread.call_function(context, args) {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

pub extern "C" fn glu_len(vm: &Thread) -> usize {
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    stack.len() as usize
}

pub extern "C" fn glu_pop(vm: &Thread, n: usize) {
    let mut context = vm.context();
    for _ in 0..n {
        context.stack.pop();
    }
}

pub extern "C" fn glu_push_int(vm: &Thread, int: VmInt) {
    Thread::push(vm, int).unwrap();
}

pub extern "C" fn glu_push_byte(vm: &Thread, b: u8) {
    Thread::push(vm, b).unwrap();
}

pub extern "C" fn glu_push_float(vm: &Thread, float: f64) {
    Thread::push(vm, float).unwrap();
}

pub extern "C" fn glu_push_bool(vm: &Thread, b: i8) {
    Thread::push(vm, b != 0).unwrap();
}

pub unsafe extern "C" fn glu_push_function(
    vm: &Thread,
    name: &u8,
    len: usize,
    function: Function,
    args: VmIndex,
) -> Error {
    let s = match str::from_utf8(slice::from_raw_parts(name, len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    match Thread::push(vm, CPrimitive::new(function, args, s)) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

/// Push a string to the stack. The string must be valid utf-8 or an error will be returned
pub unsafe extern "C" fn glu_push_string(vm: &Thread, s: &u8, len: usize) -> Error {
    let s = match str::from_utf8(slice::from_raw_parts(s, len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    match s.push(vm, &mut vm.context()) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

/// Push a string to the stack. If the string is not utf-8 this function will trigger undefined
/// behaviour.
pub unsafe extern "C" fn glu_push_string_unchecked(vm: &Thread, s: &u8, len: usize) -> Error {
    let s = str::from_utf8_unchecked(slice::from_raw_parts(s, len));
    match s.push(vm, &mut vm.context()) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

#[cfg(feature = "c_void")]
pub extern "C" fn glu_push_light_userdata(vm: &Thread, data: *mut libc::c_void) {
    Thread::push(vm, data as usize).unwrap()
}

pub extern "C" fn glu_get_byte(vm: &Thread, index: VmIndex, out: &mut u8) -> Error {
    get_value(vm, index, out)
}

pub extern "C" fn glu_get_int(vm: &Thread, index: VmIndex, out: &mut VmInt) -> Error {
    get_value(vm, index, out)
}

pub extern "C" fn glu_get_float(vm: &Thread, index: VmIndex, out: &mut f64) -> Error {
    get_value(vm, index, out)
}

pub extern "C" fn glu_get_bool(vm: &Thread, index: VmIndex, out: &mut i8) -> Error {
    let mut b = false;
    let err = get_value(vm, index, &mut b);
    if err == Error::Ok {
        *out = b as i8;
    }
    err
}

/// The returned string is garbage collected and may not be valid after the string is removed from
/// its slot in the stack
pub unsafe extern "C" fn glu_get_string(
    vm: &Thread,
    index: VmIndex,
    out: &mut *const u8,
    out_len: &mut usize,
) -> Error {
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    match stack
        .get_variants(index)
        .map(|value| <&str>::from_value(vm, value))
    {
        Some(value) => {
            *out = &*value.as_ptr();
            *out_len = value.len();
            Error::Ok
        }
        None => Error::Unknown,
    }
}

#[cfg(feature = "c_void")]
pub extern "C" fn glu_get_light_userdata(
    vm: &Thread,
    index: VmIndex,
    out: &mut *mut libc::c_void,
) -> Error {
    let mut userdata = 0usize;
    let err = get_value(vm, index, &mut userdata);
    if err == Error::Ok {
        *out = userdata as *mut libc::c_void;
    }
    err
}

fn get_value<T>(vm: &Thread, index: VmIndex, out: &mut T) -> Error
where
    T: for<'vm> Getable<'vm>,
{
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    match stack
        .get_variants(index)
        .map(|value| T::from_value(vm, value))
    {
        Some(value) => {
            *out = value;
            Error::Ok
        }
        None => Error::Unknown,
    }
}

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

    use gluon::vm::thread::{Status, Thread};

    use std::ptr;
    use std::str;
    use std::slice;

    #[test]
    fn push_pop() {
        unsafe {
            let vm = &*glu_new_vm();

            glu_push_int(vm, 123);
            glu_push_float(vm, 3.14);
            let s = "test";
            glu_push_string(vm, &s.as_bytes()[0], s.len());
            glu_push_bool(vm, 1);
            glu_push_byte(vm, 128);

            let mut int = 0;
            assert_eq!(glu_get_int(vm, 0, &mut int), Error::Ok);
            assert_eq!(int, 123);

            let mut float = 0.0;
            assert_eq!(glu_get_float(vm, 1, &mut float), Error::Ok);
            assert_eq!(float, 3.14);

            let mut string_ptr = ptr::null();
            let mut string_len = 0;
            assert_eq!(
                glu_get_string(vm, 2, &mut string_ptr, &mut string_len),
                Error::Ok
            );
            assert_eq!(
                str::from_utf8(slice::from_raw_parts(string_ptr, string_len)),
                Ok("test")
            );

            let mut b = 0;
            assert_eq!(glu_get_bool(vm, 3, &mut b), Error::Ok);
            assert_eq!(b, 1);

            let mut b = 0;
            assert_eq!(glu_get_byte(vm, 4, &mut b), Error::Ok);
            assert_eq!(b, 128);

            assert_eq!(glu_len(vm), 5);

            glu_pop(vm, 4);

            glu_free_vm(vm);
        }
    }

    #[cfg(feature = "c_void")]
    #[test]
    fn push_userdata() {
        unsafe {
            let vm = &*glu_new_vm();

            let x = 123i32;
            glu_push_light_userdata(vm, &x as *const i32 as *mut ::libc::c_void);
            let mut p = ptr::null_mut();
            assert_eq!(glu_get_light_userdata(vm, 0, &mut p), Error::Ok);
            assert_eq!(*(p as *const i32), 123);
        }
    }

    #[test]
    fn call_function() {
        extern "C" fn mult(vm: &Thread) -> Status {
            let mut l = 0.0;
            assert_eq!(glu_get_float(vm, 0, &mut l), Error::Ok);
            let mut r = 0.0;
            assert_eq!(glu_get_float(vm, 1, &mut r), Error::Ok);
            glu_push_float(vm, l * r);
            Status::Ok
        }

        unsafe {
            let vm = &*glu_new_vm();
            let name = "mult";
            glu_push_function(vm, &name.as_bytes()[0], name.len(), mult, 2);
            glu_push_float(vm, 12.0);
            glu_push_float(vm, 3.0);

            assert_eq!(glu_call_function(vm, 2), Error::Ok);
            let mut result = 0.0;
            assert_eq!(glu_get_float(vm, 0, &mut result), Error::Ok);
            assert_eq!(result, 36.0);

            glu_free_vm(vm);
        }
    }
}
<commit_msg>fix(c-api): Mark C functions as no_mangle<commit_after>//! A (WIP) C API allowing use of gluon in other langauges than Rust.
#![doc(html_root_url = "https://docs.rs/gluon_c-api/0.7.0")] // # GLUON

extern crate gluon;
#[cfg(feature = "c_void")]
extern crate libc;

use std::str;
use std::slice;

use gluon::vm::api::{CPrimitive, Getable, Hole, OpaqueValue, Pushable};
use gluon::vm::types::{VmIndex, VmInt};
use gluon::vm::thread::{RootedThread, Status, Thread, ThreadInternal};

use gluon::Compiler;

pub type Function = extern "C" fn(&Thread) -> Status;

// TODO What should the c api return as errors
// TODO How should error messages be returned
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum Error {
    Ok,
    Unknown,
}

#[no_mangle]
pub extern "C" fn glu_new_vm() -> *const Thread {
    let vm = RootedThread::new();
    vm.into_raw()
}

#[no_mangle]
pub unsafe extern "C" fn glu_free_vm(vm: &Thread) {
    RootedThread::from_raw(vm);
}

#[no_mangle]
pub unsafe extern "C" fn glu_run_expr(
    vm: &Thread,
    module: &u8,
    module_len: usize,
    expr: &u8,
    expr_len: usize,
) -> Error {
    let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let result = Compiler::new().run_expr::<OpaqueValue<&Thread, Hole>>(&vm, module, expr);
    match result {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

#[no_mangle]
pub unsafe extern "C" fn glu_load_script(
    vm: &Thread,
    module: &u8,
    module_len: usize,
    expr: &u8,
    expr_len: usize,
) -> Error {
    let module = match str::from_utf8(slice::from_raw_parts(module, module_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let expr = match str::from_utf8(slice::from_raw_parts(expr, expr_len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    let result = Compiler::new().load_script(vm, module, expr);
    match result {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

#[no_mangle]
pub extern "C" fn glu_call_function(thread: &Thread, args: VmIndex) -> Error {
    let context = thread.context();
    match thread.call_function(context, args) {
        Ok(_) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

#[no_mangle]
pub extern "C" fn glu_len(vm: &Thread) -> usize {
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    stack.len() as usize
}

#[no_mangle]
pub extern "C" fn glu_pop(vm: &Thread, n: usize) {
    let mut context = vm.context();
    for _ in 0..n {
        context.stack.pop();
    }
}

#[no_mangle]
pub extern "C" fn glu_push_int(vm: &Thread, int: VmInt) {
    Thread::push(vm, int).unwrap();
}

#[no_mangle]
pub extern "C" fn glu_push_byte(vm: &Thread, b: u8) {
    Thread::push(vm, b).unwrap();
}

#[no_mangle]
pub extern "C" fn glu_push_float(vm: &Thread, float: f64) {
    Thread::push(vm, float).unwrap();
}

#[no_mangle]
pub extern "C" fn glu_push_bool(vm: &Thread, b: i8) {
    Thread::push(vm, b != 0).unwrap();
}

#[no_mangle]
pub unsafe extern "C" fn glu_push_function(
    vm: &Thread,
    name: &u8,
    len: usize,
    function: Function,
    args: VmIndex,
) -> Error {
    let s = match str::from_utf8(slice::from_raw_parts(name, len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    match Thread::push(vm, CPrimitive::new(function, args, s)) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

/// Push a string to the stack. The string must be valid utf-8 or an error will be returned
#[no_mangle]
pub unsafe extern "C" fn glu_push_string(vm: &Thread, s: &u8, len: usize) -> Error {
    let s = match str::from_utf8(slice::from_raw_parts(s, len)) {
        Ok(s) => s,
        Err(_) => return Error::Unknown,
    };
    match s.push(vm, &mut vm.context()) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

/// Push a string to the stack. If the string is not utf-8 this function will trigger undefined
/// behaviour.
#[no_mangle]
pub unsafe extern "C" fn glu_push_string_unchecked(vm: &Thread, s: &u8, len: usize) -> Error {
    let s = str::from_utf8_unchecked(slice::from_raw_parts(s, len));
    match s.push(vm, &mut vm.context()) {
        Ok(()) => Error::Ok,
        Err(_) => Error::Unknown,
    }
}

#[cfg(feature = "c_void")]
#[no_mangle]
pub extern "C" fn glu_push_light_userdata(vm: &Thread, data: *mut libc::c_void) {
    Thread::push(vm, data as usize).unwrap()
}

#[no_mangle]
pub extern "C" fn glu_get_byte(vm: &Thread, index: VmIndex, out: &mut u8) -> Error {
    get_value(vm, index, out)
}

#[no_mangle]
pub extern "C" fn glu_get_int(vm: &Thread, index: VmIndex, out: &mut VmInt) -> Error {
    get_value(vm, index, out)
}

#[no_mangle]
pub extern "C" fn glu_get_float(vm: &Thread, index: VmIndex, out: &mut f64) -> Error {
    get_value(vm, index, out)
}

#[no_mangle]
pub extern "C" fn glu_get_bool(vm: &Thread, index: VmIndex, out: &mut i8) -> Error {
    let mut b = false;
    let err = get_value(vm, index, &mut b);
    if err == Error::Ok {
        *out = b as i8;
    }
    err
}

/// The returned string is garbage collected and may not be valid after the string is removed from
/// its slot in the stack
#[no_mangle]
pub unsafe extern "C" fn glu_get_string(
    vm: &Thread,
    index: VmIndex,
    out: &mut *const u8,
    out_len: &mut usize,
) -> Error {
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    match stack
        .get_variants(index)
        .map(|value| <&str>::from_value(vm, value))
    {
        Some(value) => {
            *out = &*value.as_ptr();
            *out_len = value.len();
            Error::Ok
        }
        None => Error::Unknown,
    }
}

#[cfg(feature = "c_void")]
#[no_mangle]
pub extern "C" fn glu_get_light_userdata(
    vm: &Thread,
    index: VmIndex,
    out: &mut *mut libc::c_void,
) -> Error {
    let mut userdata = 0usize;
    let err = get_value(vm, index, &mut userdata);
    if err == Error::Ok {
        *out = userdata as *mut libc::c_void;
    }
    err
}

fn get_value<T>(vm: &Thread, index: VmIndex, out: &mut T) -> Error
where
    T: for<'vm> Getable<'vm>,
{
    let mut context = vm.context();
    let stack = context.stack.current_frame();
    match stack
        .get_variants(index)
        .map(|value| T::from_value(vm, value))
    {
        Some(value) => {
            *out = value;
            Error::Ok
        }
        None => Error::Unknown,
    }
}

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

    use gluon::vm::thread::{Status, Thread};

    use std::ptr;
    use std::str;
    use std::slice;

    #[test]
    fn push_pop() {
        unsafe {
            let vm = &*glu_new_vm();

            glu_push_int(vm, 123);
            glu_push_float(vm, 3.14);
            let s = "test";
            glu_push_string(vm, &s.as_bytes()[0], s.len());
            glu_push_bool(vm, 1);
            glu_push_byte(vm, 128);

            let mut int = 0;
            assert_eq!(glu_get_int(vm, 0, &mut int), Error::Ok);
            assert_eq!(int, 123);

            let mut float = 0.0;
            assert_eq!(glu_get_float(vm, 1, &mut float), Error::Ok);
            assert_eq!(float, 3.14);

            let mut string_ptr = ptr::null();
            let mut string_len = 0;
            assert_eq!(
                glu_get_string(vm, 2, &mut string_ptr, &mut string_len),
                Error::Ok
            );
            assert_eq!(
                str::from_utf8(slice::from_raw_parts(string_ptr, string_len)),
                Ok("test")
            );

            let mut b = 0;
            assert_eq!(glu_get_bool(vm, 3, &mut b), Error::Ok);
            assert_eq!(b, 1);

            let mut b = 0;
            assert_eq!(glu_get_byte(vm, 4, &mut b), Error::Ok);
            assert_eq!(b, 128);

            assert_eq!(glu_len(vm), 5);

            glu_pop(vm, 4);

            glu_free_vm(vm);
        }
    }

    #[cfg(feature = "c_void")]
    #[test]
    fn push_userdata() {
        unsafe {
            let vm = &*glu_new_vm();

            let x = 123i32;
            glu_push_light_userdata(vm, &x as *const i32 as *mut ::libc::c_void);
            let mut p = ptr::null_mut();
            assert_eq!(glu_get_light_userdata(vm, 0, &mut p), Error::Ok);
            assert_eq!(*(p as *const i32), 123);
        }
    }

    #[test]
    fn call_function() {
        extern "C" fn mult(vm: &Thread) -> Status {
            let mut l = 0.0;
            assert_eq!(glu_get_float(vm, 0, &mut l), Error::Ok);
            let mut r = 0.0;
            assert_eq!(glu_get_float(vm, 1, &mut r), Error::Ok);
            glu_push_float(vm, l * r);
            Status::Ok
        }

        unsafe {
            let vm = &*glu_new_vm();
            let name = "mult";
            glu_push_function(vm, &name.as_bytes()[0], name.len(), mult, 2);
            glu_push_float(vm, 12.0);
            glu_push_float(vm, 3.0);

            assert_eq!(glu_call_function(vm, 2), Error::Ok);
            let mut result = 0.0;
            assert_eq!(glu_get_float(vm, 0, &mut result), Error::Ok);
            assert_eq!(result, 36.0);

            glu_free_vm(vm);
        }
    }
}
<|endoftext|>


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

extern crate time;

use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::Write;

/*================================================================================================*/
/*------ENUMS-------------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The log level enum
///
/// It is used with the logger struct, and is used to determine the severity of the message.
#[derive (Copy, Clone)]
pub enum LogLevel {

    /// Used for logging debug messages (stripped out of release builds)
    Debug (&'static str),
    /// Used for normal logging messages
    Info (&'static str),
    /// Used for warnings (non critical errors)
    Warning (&'static str),
    /// Used for critical errors
    Error (&'static str)
}

/*================================================================================================*/
/*------STRUCTS-----------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The logger struct.
///
/// It allows for basic logging capabilities.
/// It can currently log strings, but others will be added in the future.
pub struct Logger {

    // Private
    log_file    : Option <BufWriter <File>>,
    is_enabled  : bool,
    is_logging  : bool
}

/*================================================================================================*/
/*------PUBLIC STATIC FUNCTIONS-------------------------------------------------------------------*/
/*================================================================================================*/

impl Logger {

    /// Creates a new logger instance
    ///
    /// Logging is disabled on faliure.
    ///
    /// # Examples
    /// ```
    /// let logger = Logger::new ("log.txt");
    pub fn new (log_file_path : &str) -> Logger {

        match File::create (&log_file_path) {

            Ok (file) => Logger {log_file   : Some (BufWriter::new (file)),
                                 is_enabled : true,
                                 is_logging : false},

            Err (_) => {

                println! ("{}\n{}",
                          "WARNING: Log file could not be created",
                          "         Logging has been disabled\n");

                Logger {log_file   : None,
                        is_enabled : false,
                        is_logging : false}
            }
        }
    }

/*================================================================================================*/
/*------PUBLIC FUNCTIONS--------------------------------------------------------------------------*/
/*================================================================================================*/

    /// Starts file logging
    pub fn begin_log (&mut self) {

        // Check if enabled
        if self.is_enabled {

            self.is_logging = true;
            self.log_file.as_mut ().unwrap ().write ("---BEGIN LOG---\n\n".as_bytes ()).unwrap ();
        }
    }

/*================================================================================================*/

    /// Logs a message
    ///
    /// # Examples
    /// ```
    /// let mut logger = Logger::new ("log.txt");
    ///
    /// logger.log (LogLevel::Debug   ("This is a log entry")) // Debug
    /// logger.log (LogLevel::Message ("This is a log entry")) // Message
    /// logger.log (LogLevel::Warning ("This is a log entry")) // Warning
    /// logger.log (LogLevel::Error   ("This is a log entry")) // Error
    pub fn log (&mut self, log_level : LogLevel) {

        // Check if logger is enabled and logging
        if self.is_enabled && self.is_logging {

            // Get the buffer from the logger struct, and get current time
            let buffer       = self.log_file.as_mut ().unwrap ();
            let current_time = time::now ();

            match log_level {

                // Debug
                LogLevel::Debug (m) => {

                    // Only call if run in debug
                    if cfg! (debug_assertions) {

                        let log_string = format! ("DEBUG   ({}) {}\n", current_time.asctime (), m);
                        buffer.write (log_string.as_bytes ()).unwrap ();
                    }
                },

                // Message
                LogLevel::Info (m) => {

                    let log_string = format! ("INFO    ({}) {}\n", current_time.asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                },

                // Warning
                LogLevel::Warning (m) => {

                    let log_string = format! ("WARNING ({}) {}\n", current_time.asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                },

                // Error
                LogLevel::Error (m) => {

                    let log_string = format! ("ERROR   ({}) {}\n", current_time.asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                }
            }
        }
    }

/*================================================================================================*/

    /// Stops file logging
    pub fn end_log (&mut self) {

        // Check if enabled
        if self.is_enabled {

            self.is_logging = false;
            self.log_file.as_mut ().unwrap ().write ("\n---END LOG---\n".as_bytes ()).unwrap ();
        }
    }
}
<commit_msg>Removed unrequired variable<commit_after>/*================================================================================================*/
// Copyright 2016 Kyle Finlay
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*================================================================================================*/

extern crate time;

use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::Write;

/*================================================================================================*/
/*------ENUMS-------------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The log level enum
///
/// It is used with the logger struct, and is used to determine the severity of the message.
#[derive (Copy, Clone)]
pub enum LogLevel {

    /// Used for logging debug messages (stripped out of release builds)
    Debug (&'static str),
    /// Used for normal logging messages
    Info (&'static str),
    /// Used for warnings (non critical errors)
    Warning (&'static str),
    /// Used for critical errors
    Error (&'static str)
}

/*================================================================================================*/
/*------STRUCTS-----------------------------------------------------------------------------------*/
/*================================================================================================*/

/// The logger struct.
///
/// It allows for basic logging capabilities.
/// It can currently log strings, but others will be added in the future.
pub struct Logger {

    // Private
    log_file    : Option <BufWriter <File>>,
    is_enabled  : bool,
    is_logging  : bool
}

/*================================================================================================*/
/*------PUBLIC STATIC FUNCTIONS-------------------------------------------------------------------*/
/*================================================================================================*/

impl Logger {

    /// Creates a new logger instance
    ///
    /// Logging is disabled on faliure.
    ///
    /// # Examples
    /// ```
    /// let logger = Logger::new ("log.txt");
    pub fn new (log_file_path : &str) -> Logger {

        match File::create (&log_file_path) {

            Ok (file) => Logger {log_file   : Some (BufWriter::new (file)),
                                 is_enabled : true,
                                 is_logging : false},

            Err (_) => {

                println! ("{}\n{}",
                          "WARNING: Log file could not be created",
                          "         Logging has been disabled\n");

                Logger {log_file   : None,
                        is_enabled : false,
                        is_logging : false}
            }
        }
    }

/*================================================================================================*/
/*------PUBLIC FUNCTIONS--------------------------------------------------------------------------*/
/*================================================================================================*/

    /// Starts file logging
    pub fn begin_log (&mut self) {

        // Check if enabled
        if self.is_enabled {

            self.is_logging = true;
            self.log_file.as_mut ().unwrap ().write ("---BEGIN LOG---\n\n".as_bytes ()).unwrap ();
        }
    }

/*================================================================================================*/

    /// Logs a message
    ///
    /// # Examples
    /// ```
    /// let mut logger = Logger::new ("log.txt");
    ///
    /// logger.log (LogLevel::Debug   ("This is a log entry")) // Debug
    /// logger.log (LogLevel::Message ("This is a log entry")) // Message
    /// logger.log (LogLevel::Warning ("This is a log entry")) // Warning
    /// logger.log (LogLevel::Error   ("This is a log entry")) // Error
    pub fn log (&mut self, log_level : LogLevel) {

        // Check if logger is enabled and logging
        if self.is_enabled && self.is_logging {

            // Get the buffer from the logger struct
            let buffer = self.log_file.as_mut ().unwrap ();

            match log_level {

                // Debug
                LogLevel::Debug (m) => {

                    // Only call if run in debug
                    if cfg! (debug_assertions) {

                        let log_string = format! ("DEBUG   ({}) {}\n", time::now ().asctime (), m);
                        buffer.write (log_string.as_bytes ()).unwrap ();
                    }
                },

                // Message
                LogLevel::Info (m) => {

                    let log_string = format! ("INFO    ({}) {}\n", time::now ().asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                },

                // Warning
                LogLevel::Warning (m) => {

                    let log_string = format! ("WARNING ({}) {}\n", time::now ().asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                },

                // Error
                LogLevel::Error (m) => {

                    let log_string = format! ("ERROR   ({}) {}\n", time::now ().asctime (), m);
                    buffer.write (log_string.as_bytes ()).unwrap ();
                }
            }
        }
    }

/*================================================================================================*/

    /// Stops file logging
    pub fn end_log (&mut self) {

        // Check if enabled
        if self.is_enabled {

            self.is_logging = false;
            self.log_file.as_mut ().unwrap ().write ("\n---END LOG---\n".as_bytes ()).unwrap ();
        }
    }
}
<|endoftext|>