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

use proc_macro2::TokenStream;
use syn;
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};

// $(#[$outer:meta])*
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
//     $(
//         $(#[$inner:ident $($args:tt)*])*
//         const $Flag:ident = $value:expr;
//     )+
// }
#[derive(Debug)]
pub struct Bitflags {
    attrs: Vec<syn::Attribute>,
    vis: syn::Visibility,
    struct_token: Token![struct],
    name: syn::Ident,
    colon_token: Token![:],
    repr: syn::Type,
    flags: Flags,
}

impl Bitflags {
    pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) {
        let Bitflags {
            ref attrs,
            ref vis,
            ref name,
            ref repr,
            ref flags,
            ..
        } = *self;

        let struct_ = parse_quote! {
            /// cbindgen:internal-derive-bitflags=true
            #(#attrs)*
            #vis struct #name {
                bits: #repr,
            }
        };

        let consts = flags.expand(name);
        let impl_ = parse_quote! {
            impl #name {
                #consts
            }
        };

        (struct_, impl_)
    }
}

impl Parse for Bitflags {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Ok(Self {
            attrs: input.call(syn::Attribute::parse_outer)?,
            vis: input.parse()?,
            struct_token: input.parse()?,
            name: input.parse()?,
            colon_token: input.parse()?,
            repr: input.parse()?,
            flags: input.parse()?,
        })
    }
}

// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
#[derive(Debug)]
struct Flag {
    attrs: Vec<syn::Attribute>,
    const_token: Token![const],
    name: syn::Ident,
    equals_token: Token![=],
    value: syn::Expr,
    semicolon_token: Token![;],
}

impl Flag {
    fn expand(&self, struct_name: &syn::Ident) -> TokenStream {
        let Flag {
            ref attrs,
            ref name,
            ref value,
            ..
        } = *self;
        quote! {
            #(#attrs)*
            pub const #name : #struct_name = #struct_name { bits: #value };
        }
    }
}

impl Parse for Flag {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Ok(Self {
            attrs: input.call(syn::Attribute::parse_outer)?,
            const_token: input.parse()?,
            name: input.parse()?,
            equals_token: input.parse()?,
            value: input.parse()?,
            semicolon_token: input.parse()?,
        })
    }
}

#[derive(Debug)]
struct Flags(Vec<Flag>);

impl Parse for Flags {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        let content;
        let _ = braced!(content in input);
        let mut flags = vec![];
        while !content.is_empty() {
            flags.push(content.parse()?);
        }
        Ok(Flags(flags))
    }
}

impl Flags {
    fn expand(&self, struct_name: &syn::Ident) -> TokenStream {
        let mut ts = quote! {};
        for flag in &self.0 {
            ts.extend(flag.expand(struct_name));
        }
        ts
    }
}

pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
    let parser = Bitflags::parse;
    parser.parse2(tokens)
}
<commit_msg>Add fix for failing scenario<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use proc_macro2::TokenStream;
use syn;
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};

// $(#[$outer:meta])*
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
//     $(
//         $(#[$inner:ident $($args:tt)*])*
//         const $Flag:ident = $value:expr;
//     )+
// }
#[derive(Debug)]
pub struct Bitflags {
    attrs: Vec<syn::Attribute>,
    vis: syn::Visibility,
    struct_token: Token![struct],
    name: syn::Ident,
    colon_token: Token![:],
    repr: syn::Type,
    flags: Flags,
}

impl Bitflags {
    pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) {
        let Bitflags {
            ref attrs,
            ref vis,
            ref name,
            ref repr,
            ref flags,
            ..
        } = *self;

        let struct_ = parse_quote! {
            /// cbindgen:internal-derive-bitflags=true
            #(#attrs)*
            #vis struct #name {
                bits: #repr,
            }
        };

        let consts = flags.expand(name, repr);
        let impl_ = parse_quote! {
            impl #name {
                #consts
            }
        };

        (struct_, impl_)
    }
}

impl Parse for Bitflags {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Ok(Self {
            attrs: input.call(syn::Attribute::parse_outer)?,
            vis: input.parse()?,
            struct_token: input.parse()?,
            name: input.parse()?,
            colon_token: input.parse()?,
            repr: input.parse()?,
            flags: input.parse()?,
        })
    }
}

// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
#[derive(Debug)]
struct Flag {
    attrs: Vec<syn::Attribute>,
    const_token: Token![const],
    name: syn::Ident,
    equals_token: Token![=],
    value: syn::Expr,
    semicolon_token: Token![;],
}

impl Flag {
    fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
        let Flag {
            ref attrs,
            ref name,
            ref value,
            ..
        } = *self;
        quote! {
            #(#attrs)*
            pub const #name : #struct_name = #struct_name { bits: (#value) as #repr };
        }
    }
}

impl Parse for Flag {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Ok(Self {
            attrs: input.call(syn::Attribute::parse_outer)?,
            const_token: input.parse()?,
            name: input.parse()?,
            equals_token: input.parse()?,
            value: input.parse()?,
            semicolon_token: input.parse()?,
        })
    }
}

#[derive(Debug)]
struct Flags(Vec<Flag>);

impl Parse for Flags {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        let content;
        let _ = braced!(content in input);
        let mut flags = vec![];
        while !content.is_empty() {
            flags.push(content.parse()?);
        }
        Ok(Flags(flags))
    }
}

impl Flags {
    fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
        let mut ts = quote! {};
        for flag in &self.0 {
            ts.extend(flag.expand(struct_name, repr));
        }
        ts
    }
}

pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
    let parser = Bitflags::parse;
    parser.parse2(tokens)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

#![deny(warnings)]

use std::process::{Command, Stdio};
use std::path::{Path, PathBuf};

pub fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    run_silent(cmd);
}

pub fn run_silent(cmd: &mut Command) {
    let status = match cmd.status() {
        Ok(status) => status,
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status));
    }
}

pub fn gnu_target(target: &str) -> String {
    match target {
        "i686-pc-windows-msvc" => "i686-pc-win32".to_string(),
        "x86_64-pc-windows-msvc" => "x86_64-pc-win32".to_string(),
        "i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(),
        "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(),
        s => s.to_string(),
    }
}

pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
    if target.contains("musl") || target.contains("msvc") {
        PathBuf::from("ar")
    } else {
        let parent = cc.parent().unwrap();
        let file = cc.file_name().unwrap().to_str().unwrap();
        for suffix in &["gcc", "cc", "clang"] {
            if let Some(idx) = file.rfind(suffix) {
                let mut file = file[..idx].to_owned();
                file.push_str(suffix);
                return parent.join(&file);
            }
        }
        parent.join(file)
    }
}

pub fn output(cmd: &mut Command) -> String {
    let output = match cmd.stderr(Stdio::inherit()).output() {
        Ok(status) => status,
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !output.status.success() {
        panic!("command did not execute successfully: {:?}\n\
                expected success, got: {}", cmd, output.status);
    }
    String::from_utf8(output.stdout).unwrap()
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    std::process::exit(1);
}
<commit_msg>whoops<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

#![deny(warnings)]

use std::process::{Command, Stdio};
use std::path::{Path, PathBuf};

pub fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    run_silent(cmd);
}

pub fn run_silent(cmd: &mut Command) {
    let status = match cmd.status() {
        Ok(status) => status,
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status));
    }
}

pub fn gnu_target(target: &str) -> String {
    match target {
        "i686-pc-windows-msvc" => "i686-pc-win32".to_string(),
        "x86_64-pc-windows-msvc" => "x86_64-pc-win32".to_string(),
        "i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(),
        "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(),
        s => s.to_string(),
    }
}

pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
    if target.contains("musl") || target.contains("msvc") {
        PathBuf::from("ar")
    } else {
        let parent = cc.parent().unwrap();
        let file = cc.file_name().unwrap().to_str().unwrap();
        for suffix in &["gcc", "cc", "clang"] {
            if let Some(idx) = file.rfind(suffix) {
                let mut file = file[..idx].to_owned();
                file.push_str("ar");
                return parent.join(&file);
            }
        }
        parent.join(file)
    }
}

pub fn output(cmd: &mut Command) -> String {
    let output = match cmd.stderr(Stdio::inherit()).output() {
        Ok(status) => status,
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !output.status.success() {
        panic!("command did not execute successfully: {:?}\n\
                expected success, got: {}", cmd, output.status);
    }
    String::from_utf8(output.stdout).unwrap()
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    std::process::exit(1);
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::prelude::*;
use std::io;

use term::Attr;
use term::color::{Color, BLACK, RED, GREEN, YELLOW};
use term::{Terminal, TerminfoTerminal, color};

use self::AdequateTerminal::{NoColor, Colored};

#[derive(Clone, Copy)]
pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal {
    NoColor(Box<Write + Send>),
    Colored(Box<Terminal<Output=Box<Write + Send>> + Send>)
}

pub struct Shell {
    terminal: AdequateTerminal,
    config: ShellConfig,
}

pub struct MultiShell {
    out: Shell,
    err: Shell,
    verbose: bool
}

impl MultiShell {
    pub fn new(out: Shell, err: Shell, verbose: bool) -> MultiShell {
        MultiShell { out: out, err: err, verbose: verbose }
    }

    pub fn out(&mut self) -> &mut Shell {
        &mut self.out
    }

    pub fn err(&mut self) -> &mut Shell {
        &mut self.err
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> io::Result<()> {
        self.out().say(message, color)
    }

    pub fn status<T, U>(&mut self, status: T, message: U) -> io::Result<()>
        where T: fmt::Display, U: fmt::Display
    {
        self.out().say_status(status, message, GREEN)
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut MultiShell) -> io::Result<()>
    {
        if self.verbose { return callback(self) }
        Ok(())
    }

    pub fn concise<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut MultiShell) -> io::Result<()>
    {
        if !self.verbose { return callback(self) }
        Ok(())
    }

    pub fn error<T: ToString>(&mut self, message: T) -> io::Result<()> {
        self.err().say(message, RED)
    }

    pub fn warn<T: ToString>(&mut self, message: T) -> io::Result<()> {
        self.err().say(message, YELLOW)
    }

    pub fn set_verbose(&mut self, verbose: bool) {
        self.verbose = verbose;
    }

    pub fn get_verbose(&self) -> bool {
        self.verbose
    }
}

impl Shell {
    pub fn create(out: Box<Write + Send>, config: ShellConfig) -> Shell {
        if config.tty && config.color {
            let term = TerminfoTerminal::new(out);
            term.map(|t| Shell {
                terminal: Colored(Box::new(t)),
                config: config
            }).unwrap_or_else(|| {
                Shell { terminal: NoColor(Box::new(io::stderr())), config: config }
            })
        } else {
            Shell { terminal: NoColor(out), config: config }
        }
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut Shell) -> io::Result<()>
    {
        if self.config.verbose { return callback(self) }
        Ok(())
    }

    pub fn concise<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut Shell) -> io::Result<()>
    {
        if !self.config.verbose { return callback(self) }
        Ok(())
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> io::Result<()> {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        try!(write!(self, "{}\n", message.to_string()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }

    pub fn say_status<T, U>(&mut self, status: T, message: U, color: Color)
                            -> io::Result<()>
        where T: fmt::Display, U: fmt::Display
    {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        if self.supports_attr(Attr::Bold) { try!(self.attr(Attr::Bold)); }
        try!(write!(self, "{:>12}", status.to_string()));
        try!(self.reset());
        try!(write!(self, " {}\n", message));
        try!(self.flush());
        Ok(())
    }

    fn fg(&mut self, color: color::Color) -> io::Result<bool> {
        match self.terminal {
            Colored(ref mut c) => c.fg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
        match self.terminal {
            Colored(ref mut c) => c.attr(attr),
            NoColor(_) => Ok(false)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Colored(ref c) => c.supports_attr(attr),
            NoColor(_) => false
        }
    }

    fn reset(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.reset().map(|_| ()),
            NoColor(_) => Ok(())
        }
    }
}

impl Write for Shell {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.terminal {
            Colored(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}<commit_msg>Added EOF to shell.rs<commit_after>use std::fmt;
use std::io::prelude::*;
use std::io;

use term::Attr;
use term::color::{Color, BLACK, RED, GREEN, YELLOW};
use term::{Terminal, TerminfoTerminal, color};

use self::AdequateTerminal::{NoColor, Colored};

#[derive(Clone, Copy)]
pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal {
    NoColor(Box<Write + Send>),
    Colored(Box<Terminal<Output=Box<Write + Send>> + Send>)
}

pub struct Shell {
    terminal: AdequateTerminal,
    config: ShellConfig,
}

pub struct MultiShell {
    out: Shell,
    err: Shell,
    verbose: bool
}

impl MultiShell {
    pub fn new(out: Shell, err: Shell, verbose: bool) -> MultiShell {
        MultiShell { out: out, err: err, verbose: verbose }
    }

    pub fn out(&mut self) -> &mut Shell {
        &mut self.out
    }

    pub fn err(&mut self) -> &mut Shell {
        &mut self.err
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> io::Result<()> {
        self.out().say(message, color)
    }

    pub fn status<T, U>(&mut self, status: T, message: U) -> io::Result<()>
        where T: fmt::Display, U: fmt::Display
    {
        self.out().say_status(status, message, GREEN)
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut MultiShell) -> io::Result<()>
    {
        if self.verbose { return callback(self) }
        Ok(())
    }

    pub fn concise<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut MultiShell) -> io::Result<()>
    {
        if !self.verbose { return callback(self) }
        Ok(())
    }

    pub fn error<T: ToString>(&mut self, message: T) -> io::Result<()> {
        self.err().say(message, RED)
    }

    pub fn warn<T: ToString>(&mut self, message: T) -> io::Result<()> {
        self.err().say(message, YELLOW)
    }

    pub fn set_verbose(&mut self, verbose: bool) {
        self.verbose = verbose;
    }

    pub fn get_verbose(&self) -> bool {
        self.verbose
    }
}

impl Shell {
    pub fn create(out: Box<Write + Send>, config: ShellConfig) -> Shell {
        if config.tty && config.color {
            let term = TerminfoTerminal::new(out);
            term.map(|t| Shell {
                terminal: Colored(Box::new(t)),
                config: config
            }).unwrap_or_else(|| {
                Shell { terminal: NoColor(Box::new(io::stderr())), config: config }
            })
        } else {
            Shell { terminal: NoColor(out), config: config }
        }
    }

    pub fn verbose<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut Shell) -> io::Result<()>
    {
        if self.config.verbose { return callback(self) }
        Ok(())
    }

    pub fn concise<F>(&mut self, mut callback: F) -> io::Result<()>
        where F: FnMut(&mut Shell) -> io::Result<()>
    {
        if !self.config.verbose { return callback(self) }
        Ok(())
    }

    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> io::Result<()> {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        try!(write!(self, "{}\n", message.to_string()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }

    pub fn say_status<T, U>(&mut self, status: T, message: U, color: Color)
                            -> io::Result<()>
        where T: fmt::Display, U: fmt::Display
    {
        try!(self.reset());
        if color != BLACK { try!(self.fg(color)); }
        if self.supports_attr(Attr::Bold) { try!(self.attr(Attr::Bold)); }
        try!(write!(self, "{:>12}", status.to_string()));
        try!(self.reset());
        try!(write!(self, " {}\n", message));
        try!(self.flush());
        Ok(())
    }

    fn fg(&mut self, color: color::Color) -> io::Result<bool> {
        match self.terminal {
            Colored(ref mut c) => c.fg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn attr(&mut self, attr: Attr) -> io::Result<bool> {
        match self.terminal {
            Colored(ref mut c) => c.attr(attr),
            NoColor(_) => Ok(false)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Colored(ref c) => c.supports_attr(attr),
            NoColor(_) => false
        }
    }

    fn reset(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.reset().map(|_| ()),
            NoColor(_) => Ok(())
        }
    }
}

impl Write for Shell {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.terminal {
            Colored(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.terminal {
            Colored(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::io::prelude::*;
use std::fs::{self, File, OpenOptions};
use std::path::{Path, PathBuf};
use std::os::unix::io::AsRawFd;
use nix::c_void;
use nix::sys::mman;
use spin::RwLock as SpinRwLock;
use std::rc::Rc;
use std::slice;
use std::mem::{self, size_of};
use std::collections::VecMap;
use rustc_serialize::json;

use config::*;

#[derive(Debug)]
pub struct Message<'a> {
    pub id: u64,
    pub body: &'a[u8]
}

#[repr(packed)]
struct MessageHeader {
    // TODO: needs a hash
    id: u64,
    len: u32,
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable)]
struct QueueFileCheckpoint {
    offset: usize,
    closed: bool
}

#[derive(Debug)]
pub struct QueueFile {
    base_path: PathBuf,
    // TODO: move mmaped file abstraction
    file: File,
    file_size: usize,
    file_mmap: *mut u8,
    file_num: usize,
    // dirty_bytes: usize,
    // dirty_messages: usize,
    tail: u64,
    offset: usize,
    closed: bool,
    checkpoint_closed: bool
}

#[derive(Debug)]
pub struct QueueBackend {
    config: Rc<QueueConfig>,
    files: SpinRwLock<VecMap<Box<QueueFile>>>,
    head: u64,
    tail: u64
}

impl QueueFile {
    fn gen_base_file_path(config: &QueueConfig, file_num: usize) -> PathBuf {
        config.data_directory.join(format!("{:08}", file_num))
    }

    fn create(config: &QueueConfig, file_num: usize) -> QueueFile {
        let base_path = Self::gen_base_file_path(config, file_num);
        let data_path = base_path.with_extension(DATA_EXTENSION);
        debug!("[{}] creating data file {:?}", config.name, data_path);
        let file = OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .open(data_path).unwrap();
        // hopefully the filesystem supports sparse files
        file.set_len(config.segment_size).unwrap();
        Self::new(config, file, base_path, file_num)
    }

    fn open(config: &QueueConfig, file_num: usize) -> QueueFile {
        let base_path = Self::gen_base_file_path(config, file_num);
        let data_path = base_path.with_extension(DATA_EXTENSION);
        debug!("[{}] opening data file {:?}", config.name, data_path);
        let file = OpenOptions::new()
                .read(true)
                .write(true)
                .open(data_path).unwrap();
        let mut queue_file = Self::new(config, file, base_path, file_num);
        queue_file.recover();
        queue_file
    }

    fn new(config: &QueueConfig, file: File, base_path: PathBuf, file_num: usize) -> QueueFile {
        let file_size = file.metadata().unwrap().len();
        assert_eq!(file_size, config.segment_size);

        let file_mmap = mman::mmap(
            ptr::null_mut(), file_size,
            mman::PROT_READ | mman::PROT_WRITE, mman::MAP_SHARED,
            file.as_raw_fd(), 0).unwrap() as *mut u8;

        QueueFile {
            base_path: base_path,
            file: file,
            file_size: file_size as usize,
            file_mmap: file_mmap,
            file_num: file_num,
            tail: file_num as u64 * config.segment_size,
            // dirty_messages: 0,
            // dirty_bytes: 0,
            offset: 0,
            closed: false,
            checkpoint_closed: false,
        }
    }

    fn get(&self, id: u64) -> Result<(u64, Message), bool> {
        if id >= self.tail + self.offset as u64 {
            return Err(self.closed)
        }

        let header: &MessageHeader = unsafe {
            mem::transmute(self.file_mmap.offset((id - self.tail) as isize))
        };

        // check id and possible overflow
        assert_eq!(header.id, id);
        assert!(id - self.tail + size_of::<MessageHeader>() as u64 + header.len as u64 <= self.offset as u64);

        let message_total_len = (size_of::<MessageHeader>() + header.len as usize) as u64;

        let body = unsafe {
            slice::from_raw_parts(
                self.file_mmap.offset((id - self.tail + size_of::<MessageHeader>() as u64) as isize),
                header.len as usize)
        };

        let message = Message {
            id: id,
            body: body
        };

        Ok((id + message_total_len, message))
    }

    fn append(&mut self, message: &Message) -> Option<u64> {
        let message_total_len = size_of::<MessageHeader>() + message.body.len();
        if self.offset + message_total_len > self.file_size {
            self.closed = true;
            return None
        }

        let header = MessageHeader {
            id: self.tail + self.offset as u64,
            len: message.body.len() as u32
        };

        unsafe {
            ptr::copy_nonoverlapping(
                mem::transmute(&header),
                self.file_mmap.offset(self.offset as isize),
                size_of::<MessageHeader>());
            ptr::copy_nonoverlapping(
                message.body.as_ptr(),
                self.file_mmap.offset(self.offset as isize + size_of::<MessageHeader>() as isize),
                message.body.len());
        }

        self.offset += message_total_len;
        // self.dirty_bytes += message_total_len;
        // self.dirty_messages += 1;

        Some(header.id)
    }

    fn sync(&mut self, sync: bool) {
        let flags = if sync { mman::MS_SYNC } else { mman::MS_ASYNC };
        mman::msync(self.file_mmap as *mut c_void, self.file_size as u64, flags).unwrap();
    }

    // TODO: move some of recover and checkpoint code back into the backend
    fn recover(&mut self) {
        let path = self.base_path.with_extension(CHECKPOINT_EXTENSION);
        let checkpoint: QueueFileCheckpoint = match File::open(path) {
            Ok(mut file) => {
                let mut contents = String::new();
                let _ = file.read_to_string(&mut contents);
                let state_result = json::decode(&contents);
                match state_result {
                    Ok(state) => state,
                    Err(error) => {
                        error!("[{:?}] error parsing checkpoint information: {}",
                            self.base_path, error);
                        return;
                    }
                }
            }
            Err(error) => {
                warn!("[{:?}] error reading checkpoint information: {}",
                    self.base_path, error);
                return;
            }
        };

        info!("[{:?}] checkpoint loaded: {:?}", self.base_path, checkpoint);
        self.offset = checkpoint.offset;
        self.closed = checkpoint.closed;
        self.checkpoint_closed = checkpoint.closed;

        // TODO: read forward checking the message hashes
    }

    fn checkpoint(&mut self) {
        if self.checkpoint_closed {
            return
        }

        // FIXME: reset and log stats
        let checkpoint = QueueFileCheckpoint {
            offset: self.offset,
            closed: self.closed,
        };
        self.sync(true);

        let tmp_path = self.base_path.with_extension(TMP_CHECKPOINT_EXTENSION);
        let result = File::create(&tmp_path)
            .and_then(|mut file| {
                write!(file, "{}", json::as_pretty_json(&checkpoint)).unwrap();
                file.sync_data()
            }).and_then(|_| {
                let final_path = self.base_path.with_extension(CHECKPOINT_EXTENSION);
                fs::rename(tmp_path, final_path)
            });

        match result {
            Ok(_) => {
                if checkpoint.closed {
                    self.checkpoint_closed = true;
                }
                info!("[{:?}] checkpointed: {:?}", self.base_path, checkpoint);
            }
            Err(error) => {
                warn!("[{:?}] error writing checkpoint information: {}",
                    self.base_path, error);
            }
        }
    }

    fn purge(self) {
        let mut base_path = self.base_path.clone();
        drop(self);
        base_path.set_extension(CHECKPOINT_EXTENSION);
        let _ = fs::remove_file(&base_path);
        base_path.set_extension(TMP_CHECKPOINT_EXTENSION);
        let _ = fs::remove_file(&base_path);
        base_path.set_extension(DATA_EXTENSION);
        let _ = fs::remove_file(&base_path);
    }
}

impl Drop for QueueFile {
    fn drop(&mut self) {
        mman::munmap(self.file_mmap as *mut c_void, self.file_size as u64).unwrap();
    }
}

impl QueueBackend {
    pub fn new(config: Rc<QueueConfig>, recover: bool) -> QueueBackend {
        let mut backend = QueueBackend {
            config: config,
            files: SpinRwLock::new(VecMap::new()),
            head: 0,
            tail: 0
        };
        if recover {
            backend.recover();
        }
        backend
    }

    fn recover(&mut self) {
        let result = fs::read_dir(&self.config.data_directory).map(|dir| {
            let file_nums = dir.filter_map(|entry_opt| {
                entry_opt.ok().and_then(|entry| {
                    let entry_path = entry.path();
                    match (entry_path.file_stem(), entry_path.extension()) {
                        (Some(stem), Some(ext)) if ext == CHECKPOINT_EXTENSION => {
                            stem.to_str().and_then(|s| s.parse().ok())
                        }
                        _ => None
                    }
                })
            });

            let mut locked_files = self.files.write();
            for file_num in file_nums {
                info!("[{}] recovering file: {}", self.config.name, file_num);
                let queue_file = Box::new(QueueFile::open(&self.config, file_num));
                locked_files.insert(file_num, queue_file);
            }
        });

        if let Err(error) = result {
            warn!("[{}] error while recovering: {}", self.config.name, error);
        }
    }

    /// this relies on Box beeing at the same place even if this vector is reallocated
    /// also, this doen't do any ref count, so one must make sure the mmap is alive while there
    /// are messages pointing to this QueueFile
    fn get_queue_file(&self, index: usize) -> Option<&QueueFile> {
        let files = self.files.read();
        match files.get(&index) {
            Some(file_box_ref) => unsafe {
                Some(mem::transmute(&(**file_box_ref)))
            },
            _ => None
        }
    }

    fn get_queue_file_mut(&mut self, index: usize) -> Option<&mut QueueFile> {
        unsafe { mem::transmute(self.get_queue_file(index)) }
    }

    pub fn files_count(&self) -> usize {
        self.files.read().len()
    }

    /// Put a message at the end of the Queue, return the message id if succesfull
    /// Note: it's the caller responsability to serialize write calls
    pub fn put(&mut self, message: &Message) -> Option<u64> {
        let mut head_file = (self.head / self.config.segment_size) as usize;
        let result = if let Some(q_file) = self.get_queue_file_mut(head_file) {
            q_file.append(message)
        } else {
            None
        };

        if let Some(new_head) = result {
            self.head = new_head;
            return result
        }

        if self.head != 0 {
            head_file += 1
        }

        let q_file: &mut QueueFile = {
            let mut queue_file = Box::new(QueueFile::create(
                &self.config, head_file));
            let q_file_ptr = (&mut *queue_file) as *mut QueueFile;
            assert!(self.files.write().insert(head_file, queue_file).is_none());
            unsafe { mem::transmute(q_file_ptr) }
        };

        let result = q_file.append(message);
        if let Some(new_head) = result {
            self.head = new_head;
            result
        } else {
            panic!("Can't write to a newly created file!")
        }
    }

    /// Get a new message from the Queue just after the specified tail
    pub fn get(&mut self, mut tail: u64) -> Option<(u64, Message)> {
        let mut tail_file = (tail / self.config.segment_size) as usize;
        for _ in (0..2) {
            match self.get_queue_file(tail_file) {
                Some(q_file) => {
                    match q_file.get(tail) {
                        Ok(result) => return Some(result),
                        Err(false) => return None,
                        Err(true) => {
                            // retry in the beginning of the next file
                            tail_file += 1;
                            tail = self.config.segment_size * tail_file as u64;
                        }
                    }
                }
                None => return None
            }
        }
        unreachable!();
    }

    pub fn purge(&mut self) {
        // FIXME: terribly broken as msgs may still be pointing to the QueueFiles
        let mut locked_files = self.files.write();
        for (_, queue_file) in locked_files.drain() {
            queue_file.purge();
        }
        self.tail = 0;
        self.head = 0;
    }

    pub fn checkpoint(&mut self) {
        let file_nums: Vec<_> = self.files.read().keys().collect();
        for file_num in file_nums {
            self.get_queue_file_mut(file_num).unwrap().checkpoint();
        }
    }

    pub fn gc(&mut self, smallest_tail: u64) {
        let mut file_nums = Vec::new();
        // collect files_nums until the first still open or with tail >= smallest_tail
        for (file_num, queue_file) in &*self.files.read() {
            if ! queue_file.closed || queue_file.tail < smallest_tail {
                break
            }
            file_nums.push(file_num);
        }
        // purge them
        for file_num in file_nums {
            let queue_file = self.files.write().remove(&file_num).unwrap();
            queue_file.purge();
        }
    }
}
<commit_msg>queue file uses tail/head instead of tail/offset<commit_after>use std::ptr;
use std::io::prelude::*;
use std::fs::{self, File, OpenOptions};
use std::path::{Path, PathBuf};
use std::os::unix::io::AsRawFd;
use nix::c_void;
use nix::sys::mman;
use spin::RwLock as SpinRwLock;
use std::rc::Rc;
use std::slice;
use std::mem::{self, size_of};
use std::collections::VecMap;
use rustc_serialize::json;

use config::*;

#[derive(Debug)]
pub struct Message<'a> {
    pub id: u64,
    pub body: &'a[u8]
}

#[repr(packed)]
struct MessageHeader {
    // TODO: needs a hash
    id: u64,
    len: u32,
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable)]
struct QueueFileCheckpoint {
    head: u64,
    closed: bool
}

#[derive(Debug)]
pub struct QueueFile {
    base_path: PathBuf,
    // TODO: move mmaped file abstraction
    file: File,
    file_size: u64,
    file_mmap: *mut u8,
    file_num: usize,
    // dirty_bytes: usize,
    // dirty_messages: usize,
    tail: u64,
    head: u64,
    closed: bool,
    checkpoint_closed: bool
}

#[derive(Debug)]
pub struct QueueBackend {
    config: Rc<QueueConfig>,
    files: SpinRwLock<VecMap<Box<QueueFile>>>,
    head: u64,
    tail: u64
}

impl QueueFile {
    fn gen_base_file_path(config: &QueueConfig, file_num: usize) -> PathBuf {
        config.data_directory.join(format!("{:08}", file_num))
    }

    fn create(config: &QueueConfig, file_num: usize) -> QueueFile {
        let base_path = Self::gen_base_file_path(config, file_num);
        let data_path = base_path.with_extension(DATA_EXTENSION);
        debug!("[{}] creating data file {:?}", config.name, data_path);
        let file = OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .open(data_path).unwrap();
        // hopefully the filesystem supports sparse files
        file.set_len(config.segment_size).unwrap();
        Self::new(config, file, base_path, file_num)
    }

    fn open(config: &QueueConfig, file_num: usize) -> QueueFile {
        let base_path = Self::gen_base_file_path(config, file_num);
        let data_path = base_path.with_extension(DATA_EXTENSION);
        debug!("[{}] opening data file {:?}", config.name, data_path);
        let file = OpenOptions::new()
                .read(true)
                .write(true)
                .open(data_path).unwrap();
        let mut queue_file = Self::new(config, file, base_path, file_num);
        queue_file.recover();
        queue_file
    }

    fn new(config: &QueueConfig, file: File, base_path: PathBuf, file_num: usize) -> QueueFile {
        let file_size = file.metadata().unwrap().len();
        assert_eq!(file_size, config.segment_size);

        let file_mmap = mman::mmap(
            ptr::null_mut(), file_size,
            mman::PROT_READ | mman::PROT_WRITE, mman::MAP_SHARED,
            file.as_raw_fd(), 0).unwrap() as *mut u8;

        QueueFile {
            base_path: base_path,
            file: file,
            file_size: file_size as u64,
            file_mmap: file_mmap,
            file_num: file_num,
            tail: file_num as u64 * config.segment_size,
            head: file_num as u64 * config.segment_size,
            // dirty_messages: 0,
            // dirty_bytes: 0,
            closed: false,
            checkpoint_closed: false,
        }
    }

    fn get(&self, id: u64) -> Result<(u64, Message), bool> {
        if id >= self.head {
            return Err(self.closed)
        }

        let header: &MessageHeader = unsafe {
            mem::transmute(self.file_mmap.offset((id - self.tail) as isize))
        };

        // check id and possible overflow
        assert_eq!(header.id, id);
        assert!(header.id + size_of::<MessageHeader>() as u64 + header.len as u64 <= self.head);

        let message_total_len = (size_of::<MessageHeader>() + header.len as usize) as u64;

        let body = unsafe {
            slice::from_raw_parts(
                self.file_mmap.offset((id - self.tail + size_of::<MessageHeader>() as u64) as isize),
                header.len as usize)
        };

        let message = Message {
            id: id,
            body: body
        };

        Ok((id + message_total_len, message))
    }

    fn append(&mut self, message: &Message) -> Option<u64> {
        let message_total_len = size_of::<MessageHeader>() + message.body.len();
        let file_offset = self.head - self.tail;

        if file_offset + message_total_len as u64 > self.file_size {
            self.closed = true;
            return None
        }

        let header = MessageHeader {
            id: self.head,
            len: message.body.len() as u32
        };

        unsafe {
            ptr::copy_nonoverlapping(
                mem::transmute(&header),
                self.file_mmap.offset(file_offset as isize),
                size_of::<MessageHeader>());
            ptr::copy_nonoverlapping(
                message.body.as_ptr(),
                self.file_mmap.offset(file_offset as isize + size_of::<MessageHeader>() as isize),
                message.body.len());
        }

        self.head += message_total_len as u64;
        // self.dirty_bytes += message_total_len;
        // self.dirty_messages += 1;

        Some(header.id)
    }

    fn sync(&mut self, sync: bool) {
        let flags = if sync { mman::MS_SYNC } else { mman::MS_ASYNC };
        mman::msync(self.file_mmap as *mut c_void, self.file_size as u64, flags).unwrap();
    }

    // TODO: move some of recover and checkpoint code back into the backend
    fn recover(&mut self) {
        let path = self.base_path.with_extension(CHECKPOINT_EXTENSION);
        let checkpoint: QueueFileCheckpoint = match File::open(path) {
            Ok(mut file) => {
                let mut contents = String::new();
                let _ = file.read_to_string(&mut contents);
                let state_result = json::decode(&contents);
                match state_result {
                    Ok(state) => state,
                    Err(error) => {
                        error!("[{:?}] error parsing checkpoint information: {}",
                            self.base_path, error);
                        return;
                    }
                }
            }
            Err(error) => {
                warn!("[{:?}] error reading checkpoint information: {}",
                    self.base_path, error);
                return;
            }
        };

        info!("[{:?}] checkpoint loaded: {:?}", self.base_path, checkpoint);
        self.head = checkpoint.head;
        self.closed = checkpoint.closed;
        self.checkpoint_closed = checkpoint.closed;

        // TODO: read forward checking the message hashes
    }

    fn checkpoint(&mut self) {
        if self.checkpoint_closed {
            return
        }

        // FIXME: reset and log stats
        let checkpoint = QueueFileCheckpoint {
            head: self.head,
            closed: self.closed,
        };
        self.sync(true);

        let tmp_path = self.base_path.with_extension(TMP_CHECKPOINT_EXTENSION);
        let result = File::create(&tmp_path)
            .and_then(|mut file| {
                write!(file, "{}", json::as_pretty_json(&checkpoint)).unwrap();
                file.sync_data()
            }).and_then(|_| {
                let final_path = self.base_path.with_extension(CHECKPOINT_EXTENSION);
                fs::rename(tmp_path, final_path)
            });

        match result {
            Ok(_) => {
                if checkpoint.closed {
                    self.checkpoint_closed = true;
                }
                info!("[{:?}] checkpointed: {:?}", self.base_path, checkpoint);
            }
            Err(error) => {
                warn!("[{:?}] error writing checkpoint information: {}",
                    self.base_path, error);
            }
        }
    }

    fn purge(self) {
        let mut base_path = self.base_path.clone();
        drop(self);
        base_path.set_extension(CHECKPOINT_EXTENSION);
        let _ = fs::remove_file(&base_path);
        base_path.set_extension(TMP_CHECKPOINT_EXTENSION);
        let _ = fs::remove_file(&base_path);
        base_path.set_extension(DATA_EXTENSION);
        let _ = fs::remove_file(&base_path);
    }
}

impl Drop for QueueFile {
    fn drop(&mut self) {
        mman::munmap(self.file_mmap as *mut c_void, self.file_size as u64).unwrap();
    }
}

impl QueueBackend {
    pub fn new(config: Rc<QueueConfig>, recover: bool) -> QueueBackend {
        let mut backend = QueueBackend {
            config: config,
            files: SpinRwLock::new(VecMap::new()),
            head: 0,
            tail: 0
        };
        if recover {
            backend.recover();
        }
        backend
    }

    fn recover(&mut self) {
        let mut locked_files = self.files.write();
        let result = fs::read_dir(&self.config.data_directory).map(|dir| {
            let file_nums = dir.filter_map(|entry_opt| {
                entry_opt.ok().and_then(|entry| {
                    let entry_path = entry.path();
                    match (entry_path.file_stem(), entry_path.extension()) {
                        (Some(stem), Some(ext)) if ext == CHECKPOINT_EXTENSION => {
                            stem.to_str().and_then(|s| s.parse().ok())
                        }
                        _ => None
                    }
                })
            });

            for file_num in file_nums {
                info!("[{}] recovering file: {}", self.config.name, file_num);
                let queue_file = Box::new(QueueFile::open(&self.config, file_num));
                locked_files.insert(file_num, queue_file);
            }
        });

        if let Some(first_file) = locked_files.values().next() {
            self.tail = first_file.tail
        }

        if let Some(last_file) = locked_files.values().last() {
            self.head = last_file.head;
        }

        if let Err(error) = result {
            warn!("[{}] error while recovering: {}", self.config.name, error);
        }
    }

    /// this relies on Box beeing at the same place even if this vector is reallocated
    /// also, this doen't do any ref count, so one must make sure the mmap is alive while there
    /// are messages pointing to this QueueFile
    fn get_queue_file(&self, index: usize) -> Option<&QueueFile> {
        let files = self.files.read();
        match files.get(&index) {
            Some(file_box_ref) => unsafe {
                Some(mem::transmute(&(**file_box_ref)))
            },
            _ => None
        }
    }

    fn get_queue_file_mut(&mut self, index: usize) -> Option<&mut QueueFile> {
        unsafe { mem::transmute(self.get_queue_file(index)) }
    }

    pub fn files_count(&self) -> usize {
        self.files.read().len()
    }

    /// Put a message at the end of the Queue, return the message id if succesfull
    /// Note: it's the caller responsability to serialize write calls
    pub fn put(&mut self, message: &Message) -> Option<u64> {
        let mut head_file = (self.head / self.config.segment_size) as usize;
        let result = if let Some(q_file) = self.get_queue_file_mut(head_file) {
            q_file.append(message)
        } else {
            None
        };

        if let Some(new_head) = result {
            self.head = new_head;
            return result
        }

        if self.head != 0 {
            head_file += 1
        }

        let q_file: &mut QueueFile = {
            let mut queue_file = Box::new(QueueFile::create(
                &self.config, head_file));
            let q_file_ptr = (&mut *queue_file) as *mut QueueFile;
            assert!(self.files.write().insert(head_file, queue_file).is_none());
            unsafe { mem::transmute(q_file_ptr) }
        };

        let result = q_file.append(message);
        if let Some(new_head) = result {
            self.head = new_head;
            result
        } else {
            panic!("Can't write to a newly created file!")
        }
    }

    /// Get a new message from the Queue just after the specified tail
    pub fn get(&mut self, mut tail: u64) -> Option<(u64, Message)> {
        let mut tail_file = (tail / self.config.segment_size) as usize;
        for _ in (0..2) {
            match self.get_queue_file(tail_file) {
                Some(q_file) => {
                    match q_file.get(tail) {
                        Ok(result) => return Some(result),
                        Err(false) => return None,
                        Err(true) => {
                            // retry in the beginning of the next file
                            tail_file += 1;
                            tail = self.config.segment_size * tail_file as u64;
                        }
                    }
                }
                None => return None
            }
        }
        unreachable!();
    }

    pub fn purge(&mut self) {
        // FIXME: terribly broken as msgs may still be pointing to the QueueFiles
        let mut locked_files = self.files.write();
        for (_, queue_file) in locked_files.drain() {
            queue_file.purge();
        }
        self.tail = 0;
        self.head = 0;
    }

    pub fn checkpoint(&mut self) {
        let file_nums: Vec<_> = self.files.read().keys().collect();
        for file_num in file_nums {
            self.get_queue_file_mut(file_num).unwrap().checkpoint();
        }
    }

    pub fn gc(&mut self, smallest_tail: u64) {
        let mut file_nums = Vec::new();
        // collect files_nums until the first still open or with tail >= smallest_tail
        for (file_num, queue_file) in &*self.files.read() {
            if ! queue_file.closed || queue_file.tail < smallest_tail {
                break
            }
            file_nums.push(file_num);
        }
        // purge them
        for file_num in file_nums {
            let queue_file = self.files.write().remove(&file_num).unwrap();
            queue_file.purge();
        }
    }
}
<|endoftext|>


<commit_before>//! Bazel label parsing library.
//!
//! USAGE: `label::analyze("//foo/bar:baz")
mod label_error;
use label_error::LabelError;

/// Parse and analyze given str.
///
/// TODO: validate . and .. in target name
/// TODO: validate used characters in target name
pub fn analyze(input: &'_ str) -> Result<Label<'_>> {
    let label = input;
    let (input, repository_name) = consume_repository_name(input, label)?;
    let (input, package_name) = consume_package_name(input, label)?;
    let name = consume_name(input, label)?;
    let name = match (package_name, name) {
        (None, None) => {
            return Err(LabelError(err(
                label,
                "labels must have a package and/or a name.",
            )))
        }
        (Some(package_name), None) => name_from_package(package_name),
        (_, Some(name)) => name,
    };
    Ok(Label::new(repository_name, package_name, name))
}

#[derive(Debug, PartialEq, Eq)]
pub struct Label<'s> {
    pub repository_name: Option<&'s str>,
    pub package_name: Option<&'s str>,
    pub name: &'s str,
}

type Result<T, E = LabelError> = core::result::Result<T, E>;

impl<'s> Label<'s> {
    fn new(
        repository_name: Option<&'s str>,
        package_name: Option<&'s str>,
        name: &'s str,
    ) -> Label<'s> {
        Label {
            repository_name,
            package_name,
            name,
        }
    }

    pub fn packages(&self) -> Vec<&'s str> {
        match self.package_name {
            Some(name) => name.split('/').collect(),
            None => vec![],
        }
    }
}

fn err<'s>(label: &'s str, msg: &'s str) -> String {
    let mut err_msg = label.to_string();
    err_msg.push_str(" must be a legal label; ");
    err_msg.push_str(msg);
    err_msg
}

fn consume_repository_name<'s>(
    input: &'s str,
    label: &'s str,
) -> Result<(&'s str, Option<&'s str>)> {
    if !input.starts_with('@') {
        return Ok((input, None));
    }

    let slash_pos = input
        .find("//")
        .ok_or_else(|| err(label, "labels with repository must contain //."))?;
    let repository_name = &input[1..slash_pos];
    if repository_name.is_empty() {
        return Ok((&input[1..], None));
    }
    if !repository_name
        .chars()
        .next()
        .unwrap()
        .is_ascii_alphabetic()
    {
        return Err(LabelError(err(
            label,
            "workspace names must start with a letter.",
        )));
    }
    if !repository_name
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
    {
        return Err(LabelError(err(
            label,
            "workspace names \
                may contain only A-Z, a-z, 0-9, '-', '_', and '.'.",
        )));
    }
    Ok((&input[slash_pos..], Some(repository_name)))
}

fn consume_package_name<'s>(input: &'s str, label: &'s str) -> Result<(&'s str, Option<&'s str>)> {
    let is_absolute = match input.rfind("//") {
        None => false,
        Some(0) => true,
        Some(_) => {
            return Err(LabelError(err(
                label,
                "'//' cannot appear in the middle of the label.",
            )));
        }
    };

    let (package_name, rest) = match (is_absolute, input.find(':')) {
        (false, colon_pos) if colon_pos.map_or(true, |pos| pos != 0) => {
            return Err(LabelError(err(
                label,
                "relative packages are not permitted.",
            )));
        }
        (_, colon_pos) => {
            let (input, colon_pos) = if is_absolute {
                (&input[2..], colon_pos.map(|cp| cp - 2))
            } else {
                (input, colon_pos)
            };
            match colon_pos {
                Some(colon_pos) => (&input[0..colon_pos], &input[colon_pos..]),
                None => (input, ""),
            }
        }
    };

    if package_name.is_empty() {
        return Ok((rest, None));
    }

    if !package_name.chars().all(|c| {
        c.is_ascii_alphanumeric()
            || c == '/'
            || c == '-'
            || c == '.'
            || c == ' '
            || c == '$'
            || c == '('
            || c == ')'
            || c == '_'
    }) {
        return Err(LabelError(err(
            label,
            "package names may contain only A-Z, \
        a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'.",
        )));
    }
    if package_name.ends_with('/') {
        return Err(LabelError(err(
            label,
            "package names may not end with '/'.",
        )));
    }

    if rest.is_empty() && is_absolute {
        // This label doesn't contain the target name, we have to use
        // last segment of the package name as target name.
        return Ok((
            match package_name.rfind('/') {
                Some(pos) => &package_name[pos..],
                None => package_name,
            },
            Some(package_name),
        ));
    }

    Ok((rest, Some(package_name)))
}

fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<Option<&'s str>> {
    if input.is_empty() {
        return Ok(None);
    }
    if input == ":" {
        return Err(LabelError(err(label, "empty target name.")));
    }
    let name = input
        .strip_prefix(':')
        .or_else(|| input.strip_prefix('/'))
        .unwrap_or(input);
    if name.starts_with('/') {
        return Err(LabelError(err(
            label,
            "target names may not start with '/'.",
        )));
    }
    Ok(Some(name))
}

fn name_from_package(package_name: &str) -> &str {
    package_name
        .rsplit_once('/')
        .map(|tup| tup.1)
        .unwrap_or(package_name)
}

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

    #[test]
    fn test_new() {
        assert_eq!(
            Label::new(Some("repo"), Some("foo/bar"), "baz"),
            Label {
                repository_name: Some("repo"),
                package_name: Some("foo/bar"),
                name: "baz",
            }
        );
    }

    #[test]
    fn test_repository_name_parsing() -> Result<()> {
        assert_eq!(analyze("@repo//:foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//:foo")?.repository_name, None);
        assert_eq!(analyze("//:foo")?.repository_name, None);
        assert_eq!(analyze(":foo")?.repository_name, None);

        assert_eq!(analyze("@repo//foo/bar")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo/bar")?.repository_name, None);
        assert_eq!(analyze("//foo/bar")?.repository_name, None);
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo")?.repository_name, None);
        assert_eq!(analyze("//foo")?.repository_name, None);
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(
            analyze("@foo:bar"),
            Err(LabelError(
                "@foo:bar must be a legal label; labels with repository must contain //."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("@AZab0123456789_-.//:foo")?.repository_name,
            Some("AZab0123456789_-.")
        );
        assert_eq!(
            analyze("@42//:baz"),
            Err(LabelError(
                "@42//:baz must be a legal label; workspace names must \
            start with a letter."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("@foo#//:baz"),
            Err(LabelError(
                "@foo#//:baz must be a legal label; workspace names \
            may contain only A-Z, a-z, 0-9, '-', '_', and '.'."
                    .to_string()
            ))
        );
        Ok(())
    }
    #[test]
    fn test_package_name_parsing() -> Result<()> {
        assert_eq!(analyze("//:baz/qux")?.package_name, None);
        assert_eq!(analyze(":baz/qux")?.package_name, None);

        assert_eq!(analyze("//foo:baz/qux")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo/bar:baz/qux")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo:baz/qux"),
            Err(LabelError(
                "foo:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo/bar:baz/qux"),
            Err(LabelError(
                "foo/bar:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );

        assert_eq!(analyze("//foo")?.package_name, Some("foo"));

        assert_eq!(
            analyze("foo//bar"),
            Err(LabelError(
                "foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar"),
            Err(LabelError(
                "//foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo//bar:baz"),
            Err(LabelError(
                "foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar:baz"),
            Err(LabelError(
                "//foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("//azAZ09/-. $()_:baz")?.package_name,
            Some("azAZ09/-. $()_")
        );
        assert_eq!(
            analyze("//bar#:baz"),
            Err(LabelError(
                "//bar#:baz must be a legal label; package names may contain only A-Z, \
                a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')' and '_'."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//bar/:baz"),
            Err(LabelError(
                "//bar/:baz must be a legal label; package names may not end with '/'.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(analyze("//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo")?.package_name, Some("foo"));
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_name_parsing() -> Result<()> {
        assert_eq!(analyze("//foo:baz")?.name, "baz");
        assert_eq!(analyze("//foo:baz/qux")?.name, "baz/qux");

        assert_eq!(
            analyze("//bar:"),
            Err(LabelError(
                "//bar: must be a legal label; empty target name.".to_string()
            ))
        );
        assert_eq!(analyze("//foo")?.name, "foo");

        assert_eq!(
            analyze("//bar:/foo"),
            Err(LabelError(
                "//bar:/foo must be a legal label; target names may not start with '/'."
                    .to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.name, "bar");
        assert_eq!(analyze("//foo/bar")?.name, "bar");
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.name, "foo");
        assert_eq!(analyze("//foo")?.name, "foo");
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_packages() -> Result<()> {
        assert_eq!(analyze("@repo//:baz")?.packages(), Vec::<&str>::new());
        assert_eq!(analyze("@repo//foo:baz")?.packages(), vec!["foo"]);
        assert_eq!(
            analyze("@repo//foo/bar:baz")?.packages(),
            vec!["foo", "bar"]
        );

        Ok(())
    }
}
<commit_msg>Update `//util/label` to support `+` in packages (#1654)<commit_after>//! Bazel label parsing library.
//!
//! USAGE: `label::analyze("//foo/bar:baz")
mod label_error;
use label_error::LabelError;

/// Parse and analyze given str.
///
/// TODO: validate . and .. in target name
/// TODO: validate used characters in target name
pub fn analyze(input: &'_ str) -> Result<Label<'_>> {
    let label = input;
    let (input, repository_name) = consume_repository_name(input, label)?;
    let (input, package_name) = consume_package_name(input, label)?;
    let name = consume_name(input, label)?;
    let name = match (package_name, name) {
        (None, None) => {
            return Err(LabelError(err(
                label,
                "labels must have a package and/or a name.",
            )))
        }
        (Some(package_name), None) => name_from_package(package_name),
        (_, Some(name)) => name,
    };
    Ok(Label::new(repository_name, package_name, name))
}

#[derive(Debug, PartialEq, Eq)]
pub struct Label<'s> {
    pub repository_name: Option<&'s str>,
    pub package_name: Option<&'s str>,
    pub name: &'s str,
}

type Result<T, E = LabelError> = core::result::Result<T, E>;

impl<'s> Label<'s> {
    fn new(
        repository_name: Option<&'s str>,
        package_name: Option<&'s str>,
        name: &'s str,
    ) -> Label<'s> {
        Label {
            repository_name,
            package_name,
            name,
        }
    }

    pub fn packages(&self) -> Vec<&'s str> {
        match self.package_name {
            Some(name) => name.split('/').collect(),
            None => vec![],
        }
    }
}

fn err<'s>(label: &'s str, msg: &'s str) -> String {
    let mut err_msg = label.to_string();
    err_msg.push_str(" must be a legal label; ");
    err_msg.push_str(msg);
    err_msg
}

fn consume_repository_name<'s>(
    input: &'s str,
    label: &'s str,
) -> Result<(&'s str, Option<&'s str>)> {
    if !input.starts_with('@') {
        return Ok((input, None));
    }

    let slash_pos = input
        .find("//")
        .ok_or_else(|| err(label, "labels with repository must contain //."))?;
    let repository_name = &input[1..slash_pos];
    if repository_name.is_empty() {
        return Ok((&input[1..], None));
    }
    if !repository_name
        .chars()
        .next()
        .unwrap()
        .is_ascii_alphabetic()
    {
        return Err(LabelError(err(
            label,
            "workspace names must start with a letter.",
        )));
    }
    if !repository_name
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
    {
        return Err(LabelError(err(
            label,
            "workspace names \
                may contain only A-Z, a-z, 0-9, '-', '_', and '.'.",
        )));
    }
    Ok((&input[slash_pos..], Some(repository_name)))
}

fn consume_package_name<'s>(input: &'s str, label: &'s str) -> Result<(&'s str, Option<&'s str>)> {
    let is_absolute = match input.rfind("//") {
        None => false,
        Some(0) => true,
        Some(_) => {
            return Err(LabelError(err(
                label,
                "'//' cannot appear in the middle of the label.",
            )));
        }
    };

    let (package_name, rest) = match (is_absolute, input.find(':')) {
        (false, colon_pos) if colon_pos.map_or(true, |pos| pos != 0) => {
            return Err(LabelError(err(
                label,
                "relative packages are not permitted.",
            )));
        }
        (_, colon_pos) => {
            let (input, colon_pos) = if is_absolute {
                (&input[2..], colon_pos.map(|cp| cp - 2))
            } else {
                (input, colon_pos)
            };
            match colon_pos {
                Some(colon_pos) => (&input[0..colon_pos], &input[colon_pos..]),
                None => (input, ""),
            }
        }
    };

    if package_name.is_empty() {
        return Ok((rest, None));
    }

    if !package_name.chars().all(|c| {
        c.is_ascii_alphanumeric()
            || c == '/'
            || c == '-'
            || c == '.'
            || c == ' '
            || c == '$'
            || c == '('
            || c == ')'
            || c == '_'
            || c == '+'
    }) {
        return Err(LabelError(err(
            label,
            "package names may contain only A-Z, \
        a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')', '_', and '+'.",
        )));
    }
    if package_name.ends_with('/') {
        return Err(LabelError(err(
            label,
            "package names may not end with '/'.",
        )));
    }

    if rest.is_empty() && is_absolute {
        // This label doesn't contain the target name, we have to use
        // last segment of the package name as target name.
        return Ok((
            match package_name.rfind('/') {
                Some(pos) => &package_name[pos..],
                None => package_name,
            },
            Some(package_name),
        ));
    }

    Ok((rest, Some(package_name)))
}

fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<Option<&'s str>> {
    if input.is_empty() {
        return Ok(None);
    }
    if input == ":" {
        return Err(LabelError(err(label, "empty target name.")));
    }
    let name = input
        .strip_prefix(':')
        .or_else(|| input.strip_prefix('/'))
        .unwrap_or(input);
    if name.starts_with('/') {
        return Err(LabelError(err(
            label,
            "target names may not start with '/'.",
        )));
    }
    Ok(Some(name))
}

fn name_from_package(package_name: &str) -> &str {
    package_name
        .rsplit_once('/')
        .map(|tup| tup.1)
        .unwrap_or(package_name)
}

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

    #[test]
    fn test_new() {
        assert_eq!(
            Label::new(Some("repo"), Some("foo/bar"), "baz"),
            Label {
                repository_name: Some("repo"),
                package_name: Some("foo/bar"),
                name: "baz",
            }
        );
    }

    #[test]
    fn test_repository_name_parsing() -> Result<()> {
        assert_eq!(analyze("@repo//:foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//:foo")?.repository_name, None);
        assert_eq!(analyze("//:foo")?.repository_name, None);
        assert_eq!(analyze(":foo")?.repository_name, None);

        assert_eq!(analyze("@repo//foo/bar")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo/bar")?.repository_name, None);
        assert_eq!(analyze("//foo/bar")?.repository_name, None);
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.repository_name, Some("repo"));
        assert_eq!(analyze("@//foo")?.repository_name, None);
        assert_eq!(analyze("//foo")?.repository_name, None);
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(
            analyze("@foo:bar"),
            Err(LabelError(
                "@foo:bar must be a legal label; labels with repository must contain //."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("@AZab0123456789_-.//:foo")?.repository_name,
            Some("AZab0123456789_-.")
        );
        assert_eq!(
            analyze("@42//:baz"),
            Err(LabelError(
                "@42//:baz must be a legal label; workspace names must \
            start with a letter."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("@foo#//:baz"),
            Err(LabelError(
                "@foo#//:baz must be a legal label; workspace names \
            may contain only A-Z, a-z, 0-9, '-', '_', and '.'."
                    .to_string()
            ))
        );
        Ok(())
    }
    #[test]
    fn test_package_name_parsing() -> Result<()> {
        assert_eq!(analyze("//:baz/qux")?.package_name, None);
        assert_eq!(analyze(":baz/qux")?.package_name, None);

        assert_eq!(analyze("//foo:baz/qux")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo/bar:baz/qux")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo:baz/qux"),
            Err(LabelError(
                "foo:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo/bar:baz/qux"),
            Err(LabelError(
                "foo/bar:baz/qux must be a legal label; relative packages are not permitted."
                    .to_string()
            ))
        );

        assert_eq!(analyze("//foo")?.package_name, Some("foo"));

        assert_eq!(
            analyze("foo//bar"),
            Err(LabelError(
                "foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar"),
            Err(LabelError(
                "//foo//bar must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("foo//bar:baz"),
            Err(LabelError(
                "foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//foo//bar:baz"),
            Err(LabelError(
                "//foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
                    .to_string()
            ))
        );

        assert_eq!(
            analyze("//azAZ09/-. $()_:baz")?.package_name,
            Some("azAZ09/-. $()_")
        );
        assert_eq!(
            analyze("//bar#:baz"),
            Err(LabelError(
                "//bar#:baz must be a legal label; package names may contain only A-Z, \
                a-z, 0-9, '/', '-', '.', ' ', '$', '(', ')', '_', and '+'."
                    .to_string()
            ))
        );
        assert_eq!(
            analyze("//bar/:baz"),
            Err(LabelError(
                "//bar/:baz must be a legal label; package names may not end with '/'.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(analyze("//foo/bar")?.package_name, Some("foo/bar"));
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.package_name, Some("foo"));
        assert_eq!(analyze("//foo")?.package_name, Some("foo"));
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_name_parsing() -> Result<()> {
        assert_eq!(analyze("//foo:baz")?.name, "baz");
        assert_eq!(analyze("//foo:baz/qux")?.name, "baz/qux");

        assert_eq!(
            analyze("//bar:"),
            Err(LabelError(
                "//bar: must be a legal label; empty target name.".to_string()
            ))
        );
        assert_eq!(analyze("//foo")?.name, "foo");

        assert_eq!(
            analyze("//bar:/foo"),
            Err(LabelError(
                "//bar:/foo must be a legal label; target names may not start with '/'."
                    .to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo/bar")?.name, "bar");
        assert_eq!(analyze("//foo/bar")?.name, "bar");
        assert_eq!(
            analyze("foo/bar"),
            Err(LabelError(
                "foo/bar must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        assert_eq!(analyze("@repo//foo")?.name, "foo");
        assert_eq!(analyze("//foo")?.name, "foo");
        assert_eq!(
            analyze("foo"),
            Err(LabelError(
                "foo must be a legal label; relative packages are not permitted.".to_string()
            ))
        );

        Ok(())
    }

    #[test]
    fn test_packages() -> Result<()> {
        assert_eq!(analyze("@repo//:baz")?.packages(), Vec::<&str>::new());
        assert_eq!(analyze("@repo//foo:baz")?.packages(), vec!["foo"]);
        assert_eq!(
            analyze("@repo//foo/bar:baz")?.packages(),
            vec!["foo", "bar"]
        );

        // Plus (+) is valid in packages
        assert_eq!(
            analyze("@repo//foo/bar+baz:qaz")?.packages(),
            vec!["foo", "bar+baz"]
        );

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! You can define your own filters; for more information,
//! see the top-level crate documentation.

#[cfg(feature = "serde-json")]
mod json;

#[cfg(feature = "serde-json")]
pub use self::json::json;

use error::Error::Fmt;
use num_traits::cast::NumCast;
use num_traits::Signed;
use std::fmt;

use super::Result;
use escaping::{self, MarkupDisplay};

// This is used by the code generator to decide whether a named filter is part of
// Askama or should refer to a local `filters` module. It should contain all the
// filters shipped with Askama, even the optional ones (since optional inclusion
// in the const vector based on features seems impossible right now).
pub const BUILT_IN_FILTERS: [&str; 20] = [
    "abs",
    "capitalize",
    "center",
    "e",
    "escape",
    "format",
    "indent",
    "into_isize",
    "join",
    "linebreaks",
    "linebreaksbr",
    "lower",
    "lowercase",
    "safe",
    "trim",
    "truncate",
    "upper",
    "uppercase",
    "wordcount",
    "json", // Optional feature; reserve the name anyway
];

/// Marks a string (or other `Display` type) as safe
///
/// Use this is you want to allow markup in an expression, or if you know
/// that the expression's contents don't need to be escaped.
pub fn safe<D, I>(v: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let res: MarkupDisplay<D> = v.into();
    Ok(res.mark_safe())
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape<D, I>(i: I) -> Result<MarkupDisplay<String>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let md: MarkupDisplay<D> = i.into();
    Ok(MarkupDisplay::Safe(escaping::escape(md.unsafe_string())))
}

/// Alias for the `escape()` filter
pub fn e<D, I>(i: I) -> Result<MarkupDisplay<String>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    escape(i)
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() {}

/// Replaces line breaks in plain text with appropriate HTML
///
/// A single newline becomes an HTML line break `<br>` and a new line
/// followed by a blank line becomes a paragraph break `<p>`.
pub fn linebreaks(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    let linebroken = s.replace("\n\n", "

").replace("\n", "
");

    Ok(format!("
{}
", linebroken))
}

/// Converts all newlines in a piece of plain text to HTML line breaks
pub fn linebreaksbr(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.replace("\n", "
"))
}

/// Converts to lowercase
pub fn lower(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_lowercase())
}

/// Alias for the `lower()` filter
pub fn lowercase(s: &fmt::Display) -> Result<String> {
    lower(s)
}

/// Converts to uppercase
pub fn upper(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_uppercase())
}

/// Alias for the `upper()` filter
pub fn uppercase(s: &fmt::Display) -> Result<String> {
    upper(s)
}

/// Strip leading and trailing whitespace
pub fn trim(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.trim().to_owned())
}

/// Limit string length, appends '...' if truncated
pub fn truncate(s: &fmt::Display, len: &usize) -> Result<String> {
    let mut s = s.to_string();
    if s.len() < *len {
        Ok(s)
    } else {
        s.truncate(*len);
        s.push_str("...");
        Ok(s)
    }
}

/// Indent lines with `width` spaces
pub fn indent(s: &fmt::Display, width: &usize) -> Result<String> {
    let s = s.to_string();

    let mut indented = String::new();

    for (i, c) in s.char_indices() {
        indented.push(c);

        if c == '\n' && i < s.len() - 1 {
            for _ in 0..*width {
                indented.push(' ');
            }
        }
    }

    Ok(indented)
}

/// Casts number to isize
pub fn into_isize<T>(number: T) -> Result<isize>
where
    T: NumCast,
{
    number.to_isize().ok_or(Fmt(fmt::Error))
}

/// Joins iterable into a string separated by provided argument
pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
where
    T: fmt::Display,
    I: Iterator<Item = T>,
    S: AsRef<str>,
{
    let separator: &str = separator.as_ref();

    let mut rv = String::new();

    for (num, item) in input.enumerate() {
        if num > 0 {
            rv.push_str(separator);
        }

        rv.push_str(&format!("{}", item));
    }

    Ok(rv)
}

/// Absolute value
pub fn abs<T>(number: T) -> Result<T>
where
    T: Signed,
{
    Ok(number.abs())
}

/// Capitalize a value. The first character will be uppercase, all others lowercase.
pub fn capitalize(s: &fmt::Display) -> Result<String> {
    let mut s = s.to_string();

    match s.get_mut(0..1).map(|s| {
        s.make_ascii_uppercase();
        &*s
    }) {
        None => Ok(s),
        _ => {
            let l = s.len();
            match s.get_mut(1..l).map(|s| {
                s.make_ascii_lowercase();
                &*s
            }) {
                _ => Ok(s),
            }
        }
    }
}

/// Centers the value in a field of a given width
pub fn center(s: &fmt::Display, l: usize) -> Result<String> {
    let s = s.to_string();
    let len = s.len();

    if l <= len {
        Ok(s)
    } else {
        let p = l - len;
        let q = p / 2;
        let r = p % 2;
        let mut buf = String::with_capacity(l);

        for _ in 0..q {
            buf.push(' ');
        }

        buf.push_str(&s);

        for _ in 0..q + r {
            buf.push(' ');
        }

        Ok(buf)
    }
}

/// Count the words in that string
pub fn wordcount(s: &fmt::Display) -> Result<usize> {
    let s = s.to_string();

    Ok(s.split_whitespace().count())
}

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

    #[test]
    fn test_linebreaks() {
        assert_eq!(
            linebreaks(&"Foo\nBar Baz").unwrap(),
            "
Foo
Bar Baz
"
        );
        assert_eq!(
            linebreaks(&"Foo\nBar\n\nBaz").unwrap(),
            "
Foo
Bar

Baz
"
        );
    }

    #[test]
    fn test_linebreaksbr() {
        assert_eq!(linebreaksbr(&"Foo\nBar").unwrap(), "Foo
Bar");
        assert_eq!(
            linebreaksbr(&"Foo\nBar\n\nBaz").unwrap(),
            "Foo
Bar

Baz"
        );
    }

    #[test]
    fn test_lower() {
        assert_eq!(lower(&"Foo").unwrap(), "foo");
        assert_eq!(lower(&"FOO").unwrap(), "foo");
        assert_eq!(lower(&"FooBar").unwrap(), "foobar");
        assert_eq!(lower(&"foo").unwrap(), "foo");
    }

    #[test]
    fn test_upper() {
        assert_eq!(upper(&"Foo").unwrap(), "FOO");
        assert_eq!(upper(&"FOO").unwrap(), "FOO");
        assert_eq!(upper(&"FooBar").unwrap(), "FOOBAR");
        assert_eq!(upper(&"foo").unwrap(), "FOO");
    }

    #[test]
    fn test_trim() {
        assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld");
    }

    #[test]
    fn test_indent() {
        assert_eq!(indent(&"hello", &2).unwrap(), "hello");
        assert_eq!(indent(&"hello\n", &2).unwrap(), "hello\n");
        assert_eq!(indent(&"hello\nfoo", &2).unwrap(), "hello\n  foo");
        assert_eq!(
            indent(&"hello\nfoo\n bar", &4).unwrap(),
            "hello\n    foo\n     bar"
        );
    }

    #[test]
    fn test_into_isize() {
        assert_eq!(into_isize(1).unwrap(), 1 as isize);
        assert_eq!(into_isize(1.9).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.9).unwrap(), -1 as isize);
        assert_eq!(into_isize(1.5 as f64).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.5 as f64).unwrap(), -1 as isize);
        match into_isize(INFINITY) {
            Err(Fmt(fmt::Error)) => assert!(true),
            _ => assert!(false, "Should return error of type Err(Fmt(fmt::Error))"),
        };
    }

    #[test]
    fn test_join() {
        assert_eq!(
            join((&["hello", "world"]).into_iter(), ", ").unwrap(),
            "hello, world"
        );
        assert_eq!(join((&["hello"]).into_iter(), ", ").unwrap(), "hello");

        let empty: &[&str] = &[];
        assert_eq!(join(empty.into_iter(), ", ").unwrap(), "");

        let input: Vec<String> = vec!["foo".into(), "bar".into(), "bazz".into()];
        assert_eq!(
            join((&input).into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":").unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );

        let input: &[String] = &["foo".into(), "bar".into()];
        assert_eq!(join(input.into_iter(), ":").unwrap(), "foo:bar");
        assert_eq!(join(input.into_iter(), ":".to_string()).unwrap(), "foo:bar");

        let real: String = "blah".into();
        let input: Vec<&str> = vec![&real];
        assert_eq!(join(input.into_iter(), ";").unwrap(), "blah");

        assert_eq!(
            join((&&&&&["foo", "bar"]).into_iter(), ", ").unwrap(),
            "foo, bar"
        );
    }

    #[test]
    fn test_abs() {
        assert_eq!(abs(1).unwrap(), 1);
        assert_eq!(abs(-1).unwrap(), 1);
        assert_eq!(abs(1.0).unwrap(), 1.0);
        assert_eq!(abs(-1.0).unwrap(), 1.0);
        assert_eq!(abs(1.0 as f64).unwrap(), 1.0 as f64);
        assert_eq!(abs(-1.0 as f64).unwrap(), 1.0 as f64);
    }

    #[test]
    fn test_capitalize() {
        assert_eq!(capitalize(&"foo").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"f").unwrap(), "F".to_string());
        assert_eq!(capitalize(&"fO").unwrap(), "Fo".to_string());
        assert_eq!(capitalize(&"").unwrap(), "".to_string());
        assert_eq!(capitalize(&"FoO").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"foO BAR").unwrap(), "Foo bar".to_string());
    }

    #[test]
    fn test_center() {
        assert_eq!(center(&"f", 3).unwrap(), " f ".to_string());
        assert_eq!(center(&"f", 4).unwrap(), " f  ".to_string());
        assert_eq!(center(&"foo", 1).unwrap(), "foo".to_string());
        assert_eq!(center(&"foo bar", 8).unwrap(), "foo bar ".to_string());
    }

    #[test]
    fn test_wordcount() {
        assert_eq!(wordcount(&"").unwrap(), 0);
        assert_eq!(wordcount(&" \n\t").unwrap(), 0);
        assert_eq!(wordcount(&"foo").unwrap(), 1);
        assert_eq!(wordcount(&"foo bar").unwrap(), 2);
    }
}
<commit_msg>Add f64 filter<commit_after>//! Module for built-in filter functions
//!
//! Contains all the built-in filter functions for use in templates.
//! You can define your own filters; for more information,
//! see the top-level crate documentation.

#[cfg(feature = "serde-json")]
mod json;

#[cfg(feature = "serde-json")]
pub use self::json::json;

use error::Error::Fmt;
use num_traits::cast::NumCast;
use num_traits::Signed;
use std::fmt;

use super::Result;
use escaping::{self, MarkupDisplay};

// This is used by the code generator to decide whether a named filter is part of
// Askama or should refer to a local `filters` module. It should contain all the
// filters shipped with Askama, even the optional ones (since optional inclusion
// in the const vector based on features seems impossible right now).
pub const BUILT_IN_FILTERS: [&str; 21] = [
    "abs",
    "capitalize",
    "center",
    "e",
    "escape",
    "format",
    "indent",
    "into_f64",
    "into_isize",
    "join",
    "linebreaks",
    "linebreaksbr",
    "lower",
    "lowercase",
    "safe",
    "trim",
    "truncate",
    "upper",
    "uppercase",
    "wordcount",
    "json", // Optional feature; reserve the name anyway
];

/// Marks a string (or other `Display` type) as safe
///
/// Use this is you want to allow markup in an expression, or if you know
/// that the expression's contents don't need to be escaped.
pub fn safe<D, I>(v: I) -> Result<MarkupDisplay<D>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let res: MarkupDisplay<D> = v.into();
    Ok(res.mark_safe())
}

/// Escapes `&`, `<` and `>` in strings
pub fn escape<D, I>(i: I) -> Result<MarkupDisplay<String>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    let md: MarkupDisplay<D> = i.into();
    Ok(MarkupDisplay::Safe(escaping::escape(md.unsafe_string())))
}

/// Alias for the `escape()` filter
pub fn e<D, I>(i: I) -> Result<MarkupDisplay<String>>
where
    D: fmt::Display,
    MarkupDisplay<D>: From,
{
    escape(i)
}

/// Formats arguments according to the specified format
///
/// The first argument to this filter must be a string literal (as in normal
/// Rust). All arguments are passed through to the `format!()`
/// [macro](https://doc.rust-lang.org/stable/std/macro.format.html) by
/// the Askama code generator.
pub fn format() {}

/// Replaces line breaks in plain text with appropriate HTML
///
/// A single newline becomes an HTML line break `<br>` and a new line
/// followed by a blank line becomes a paragraph break `<p>`.
pub fn linebreaks(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    let linebroken = s.replace("\n\n", "

").replace("\n", "
");

    Ok(format!("
{}
", linebroken))
}

/// Converts all newlines in a piece of plain text to HTML line breaks
pub fn linebreaksbr(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.replace("\n", "
"))
}

/// Converts to lowercase
pub fn lower(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_lowercase())
}

/// Alias for the `lower()` filter
pub fn lowercase(s: &fmt::Display) -> Result<String> {
    lower(s)
}

/// Converts to uppercase
pub fn upper(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.to_uppercase())
}

/// Alias for the `upper()` filter
pub fn uppercase(s: &fmt::Display) -> Result<String> {
    upper(s)
}

/// Strip leading and trailing whitespace
pub fn trim(s: &fmt::Display) -> Result<String> {
    let s = s.to_string();
    Ok(s.trim().to_owned())
}

/// Limit string length, appends '...' if truncated
pub fn truncate(s: &fmt::Display, len: &usize) -> Result<String> {
    let mut s = s.to_string();
    if s.len() < *len {
        Ok(s)
    } else {
        s.truncate(*len);
        s.push_str("...");
        Ok(s)
    }
}

/// Indent lines with `width` spaces
pub fn indent(s: &fmt::Display, width: &usize) -> Result<String> {
    let s = s.to_string();

    let mut indented = String::new();

    for (i, c) in s.char_indices() {
        indented.push(c);

        if c == '\n' && i < s.len() - 1 {
            for _ in 0..*width {
                indented.push(' ');
            }
        }
    }

    Ok(indented)
}

/// Casts number to f64
pub fn into_f64<T>(number: T) -> Result<f64>
where
    T: NumCast,
{
    number.to_f64().ok_or(Fmt(fmt::Error))
}

/// Casts number to isize
pub fn into_isize<T>(number: T) -> Result<isize>
where
    T: NumCast,
{
    number.to_isize().ok_or(Fmt(fmt::Error))
}

/// Joins iterable into a string separated by provided argument
pub fn join<T, I, S>(input: I, separator: S) -> Result<String>
where
    T: fmt::Display,
    I: Iterator<Item = T>,
    S: AsRef<str>,
{
    let separator: &str = separator.as_ref();

    let mut rv = String::new();

    for (num, item) in input.enumerate() {
        if num > 0 {
            rv.push_str(separator);
        }

        rv.push_str(&format!("{}", item));
    }

    Ok(rv)
}

/// Absolute value
pub fn abs<T>(number: T) -> Result<T>
where
    T: Signed,
{
    Ok(number.abs())
}

/// Capitalize a value. The first character will be uppercase, all others lowercase.
pub fn capitalize(s: &fmt::Display) -> Result<String> {
    let mut s = s.to_string();

    match s.get_mut(0..1).map(|s| {
        s.make_ascii_uppercase();
        &*s
    }) {
        None => Ok(s),
        _ => {
            let l = s.len();
            match s.get_mut(1..l).map(|s| {
                s.make_ascii_lowercase();
                &*s
            }) {
                _ => Ok(s),
            }
        }
    }
}

/// Centers the value in a field of a given width
pub fn center(s: &fmt::Display, l: usize) -> Result<String> {
    let s = s.to_string();
    let len = s.len();

    if l <= len {
        Ok(s)
    } else {
        let p = l - len;
        let q = p / 2;
        let r = p % 2;
        let mut buf = String::with_capacity(l);

        for _ in 0..q {
            buf.push(' ');
        }

        buf.push_str(&s);

        for _ in 0..q + r {
            buf.push(' ');
        }

        Ok(buf)
    }
}

/// Count the words in that string
pub fn wordcount(s: &fmt::Display) -> Result<usize> {
    let s = s.to_string();

    Ok(s.split_whitespace().count())
}

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

    #[test]
    fn test_linebreaks() {
        assert_eq!(
            linebreaks(&"Foo\nBar Baz").unwrap(),
            "
Foo
Bar Baz
"
        );
        assert_eq!(
            linebreaks(&"Foo\nBar\n\nBaz").unwrap(),
            "
Foo
Bar

Baz
"
        );
    }

    #[test]
    fn test_linebreaksbr() {
        assert_eq!(linebreaksbr(&"Foo\nBar").unwrap(), "Foo
Bar");
        assert_eq!(
            linebreaksbr(&"Foo\nBar\n\nBaz").unwrap(),
            "Foo
Bar

Baz"
        );
    }

    #[test]
    fn test_lower() {
        assert_eq!(lower(&"Foo").unwrap(), "foo");
        assert_eq!(lower(&"FOO").unwrap(), "foo");
        assert_eq!(lower(&"FooBar").unwrap(), "foobar");
        assert_eq!(lower(&"foo").unwrap(), "foo");
    }

    #[test]
    fn test_upper() {
        assert_eq!(upper(&"Foo").unwrap(), "FOO");
        assert_eq!(upper(&"FOO").unwrap(), "FOO");
        assert_eq!(upper(&"FooBar").unwrap(), "FOOBAR");
        assert_eq!(upper(&"foo").unwrap(), "FOO");
    }

    #[test]
    fn test_trim() {
        assert_eq!(trim(&" Hello\tworld\t").unwrap(), "Hello\tworld");
    }

    #[test]
    fn test_indent() {
        assert_eq!(indent(&"hello", &2).unwrap(), "hello");
        assert_eq!(indent(&"hello\n", &2).unwrap(), "hello\n");
        assert_eq!(indent(&"hello\nfoo", &2).unwrap(), "hello\n  foo");
        assert_eq!(
            indent(&"hello\nfoo\n bar", &4).unwrap(),
            "hello\n    foo\n     bar"
        );
    }

    #[test]
    fn test_into_f64() {
        assert_eq!(into_f64(1).unwrap(), 1.0 as f64);
        assert_eq!(into_f64(1.9).unwrap(), 1.9 as f64);
        assert_eq!(into_f64(-1.9).unwrap(), -1.9 as f64);
        assert_eq!(into_f64(INFINITY as f32).unwrap(), INFINITY);
        assert_eq!(into_f64(-INFINITY as f32).unwrap(), -INFINITY);
    }

    #[test]
    fn test_into_isize() {
        assert_eq!(into_isize(1).unwrap(), 1 as isize);
        assert_eq!(into_isize(1.9).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.9).unwrap(), -1 as isize);
        assert_eq!(into_isize(1.5 as f64).unwrap(), 1 as isize);
        assert_eq!(into_isize(-1.5 as f64).unwrap(), -1 as isize);
        match into_isize(INFINITY) {
            Err(Fmt(fmt::Error)) => assert!(true),
            _ => assert!(false, "Should return error of type Err(Fmt(fmt::Error))"),
        };
    }

    #[test]
    fn test_join() {
        assert_eq!(
            join((&["hello", "world"]).into_iter(), ", ").unwrap(),
            "hello, world"
        );
        assert_eq!(join((&["hello"]).into_iter(), ", ").unwrap(), "hello");

        let empty: &[&str] = &[];
        assert_eq!(join(empty.into_iter(), ", ").unwrap(), "");

        let input: Vec<String> = vec!["foo".into(), "bar".into(), "bazz".into()];
        assert_eq!(
            join((&input).into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":").unwrap(),
            "foo:bar:bazz"
        );
        assert_eq!(
            join(input.clone().into_iter(), ":".to_string()).unwrap(),
            "foo:bar:bazz"
        );

        let input: &[String] = &["foo".into(), "bar".into()];
        assert_eq!(join(input.into_iter(), ":").unwrap(), "foo:bar");
        assert_eq!(join(input.into_iter(), ":".to_string()).unwrap(), "foo:bar");

        let real: String = "blah".into();
        let input: Vec<&str> = vec![&real];
        assert_eq!(join(input.into_iter(), ";").unwrap(), "blah");

        assert_eq!(
            join((&&&&&["foo", "bar"]).into_iter(), ", ").unwrap(),
            "foo, bar"
        );
    }

    #[test]
    fn test_abs() {
        assert_eq!(abs(1).unwrap(), 1);
        assert_eq!(abs(-1).unwrap(), 1);
        assert_eq!(abs(1.0).unwrap(), 1.0);
        assert_eq!(abs(-1.0).unwrap(), 1.0);
        assert_eq!(abs(1.0 as f64).unwrap(), 1.0 as f64);
        assert_eq!(abs(-1.0 as f64).unwrap(), 1.0 as f64);
    }

    #[test]
    fn test_capitalize() {
        assert_eq!(capitalize(&"foo").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"f").unwrap(), "F".to_string());
        assert_eq!(capitalize(&"fO").unwrap(), "Fo".to_string());
        assert_eq!(capitalize(&"").unwrap(), "".to_string());
        assert_eq!(capitalize(&"FoO").unwrap(), "Foo".to_string());
        assert_eq!(capitalize(&"foO BAR").unwrap(), "Foo bar".to_string());
    }

    #[test]
    fn test_center() {
        assert_eq!(center(&"f", 3).unwrap(), " f ".to_string());
        assert_eq!(center(&"f", 4).unwrap(), " f  ".to_string());
        assert_eq!(center(&"foo", 1).unwrap(), "foo".to_string());
        assert_eq!(center(&"foo bar", 8).unwrap(), "foo bar ".to_string());
    }

    #[test]
    fn test_wordcount() {
        assert_eq!(wordcount(&"").unwrap(), 0);
        assert_eq!(wordcount(&" \n\t").unwrap(), 0);
        assert_eq!(wordcount(&"foo").unwrap(), 1);
        assert_eq!(wordcount(&"foo bar").unwrap(), 2);
    }
}
<|endoftext|>