<commit_before>use std::thread;
use std::time::{Duration, Instant};

use super::variants::{BenchmarkDirectBucket, Variant};
use criterion::{black_box, Criterion, ParameterizedBenchmark, Throughput};
use ratelimit_meter::DirectRateLimiter;

pub fn bench_all(c: &mut Criterion) {
    let id = "multi_threaded/20_threads";

    let bm = ParameterizedBenchmark::new(
        id,
        |b, ref v| {
            run_with_variants!(v, lim: BenchmarkDirectBucket, {
                let now = Instant::now();
                let ms = Duration::from_millis(20);
                let mut children = vec![];

                lim.check_at(now).unwrap();
                for _i in 0..19 {
                    let mut lim = lim.clone();
                    let mut b = b.clone();
                    children.push(thread::spawn(move || {
                        let mut i = 0;
                        b.iter(|| {
                            i += 1;
                            black_box(lim.check_at(now + (ms * i)).is_ok());
                        });
                    }));
                }
                let mut i = 0;
                b.iter(|| {
                    i += 1;
                    black_box(lim.check_at(now + (ms * i)).is_ok());
                });
                for child in children {
                    child.join().unwrap();
                }
            });
        },
        Variant::ALL,
    ).throughput(|_s| Throughput::Elements(1));
    c.bench(id, bm);
}
<commit_msg>Add a multi-threaded benchmark for keyed rate limiters too<commit_after>use std::thread;
use std::time::{Duration, Instant};

use super::variants::{BenchmarkDirectBucket, BenchmarkKeyedBucket, Variant};
use criterion::{black_box, Criterion, ParameterizedBenchmark, Throughput};

pub fn bench_all(c: &mut Criterion) {
    bench_direct(c);
    bench_keyed(c);
}

fn bench_direct(c: &mut Criterion) {
    let id = "multi_threaded/direct";

    let bm = ParameterizedBenchmark::new(
        id,
        |b, ref v| {
            run_with_variants!(v, lim: BenchmarkDirectBucket, {
                let now = Instant::now();
                let ms = Duration::from_millis(20);
                let mut children = vec![];

                for _i in 0..19 {
                    let mut lim = lim.clone();
                    let mut b = b.clone();
                    children.push(thread::spawn(move || {
                        let mut i = 0;
                        b.iter(|| {
                            i += 1;
                            black_box(lim.check_at(now + (ms * i)).is_ok());
                        });
                    }));
                }
                let mut i = 0;
                b.iter(|| {
                    i += 1;
                    black_box(lim.check_at(now + (ms * i)).is_ok());
                });
                for child in children {
                    child.join().unwrap();
                }
            });
        },
        Variant::ALL,
    ).throughput(|_s| Throughput::Elements(1));
    c.bench(id, bm);
}

fn bench_keyed(c: &mut Criterion) {
    let id = "multi_threaded/keyed";

    let bm = ParameterizedBenchmark::new(
        id,
        |b, ref v| {
            run_with_variants!(v, lim: BenchmarkKeyedBucket, {
                let now = Instant::now();
                let ms = Duration::from_millis(20);
                let mut children = vec![];

                for _i in 0..19 {
                    let mut lim = lim.clone();
                    let mut b = b.clone();
                    children.push(thread::spawn(move || {
                        let mut i = 0;
                        b.iter(|| {
                            i += 1;
                            black_box(lim.check_at(i % 100, now + (ms * i)).is_ok());
                        });
                    }));
                }
                let mut i = 0;
                b.iter(|| {
                    i += 1;
                    black_box(lim.check_at(i % 100, now + (ms * i)).is_ok());
                });
                for child in children {
                    child.join().unwrap();
                }
            });
        },
        Variant::ALL,
    ).throughput(|_s| Throughput::Elements(1));
    c.bench(id, bm);
}
<|endoftext|>


<commit_before>use crate::noise_fns::NoiseFn;

/// Noise function that outputs a checkerboard pattern.
///
/// This noise function can take one input, size, and outputs 2size-sized
/// blocks of alternating values. The values of these blocks alternate between
/// -1.0 and 1.0.
///
/// This noise function is not very useful by itself, but it can be used for
/// debugging purposes.
#[derive(Clone, Copy, Debug)]
pub struct Checkerboard {
    // Controls the size of the block in 2^(size).
    size: usize,
}

impl Checkerboard {
    const DEFAULT_SIZE: usize = 0;

    /// Controls the size of the block in 2^(size) units.
    pub fn new(size: usize) -> Self {
        Self { size: 1 << size }
    }

    pub fn set_size(self, size: usize) -> Self {
        Self { size: 1 << size }
    }

    pub fn size(self) -> usize {
        self.size
    }
}

impl Default for Checkerboard {
    fn default() -> Self {
        Self {
            size: 1 << Checkerboard::DEFAULT_SIZE,
        }
    }
}

impl<const N: usize> NoiseFn<f64, N> for Checkerboard {
    fn get(&self, point: [f64; N]) -> f64 {
        let result = point
            .iter()
            .map(|&a| a.floor() as usize)
            .fold(0, |a, b| (a & self.size) ^ (b & self.size));

        if result > 0 {
            -1.0
        } else {
            1.0
        }
    }
}
<commit_msg>Fix bug in checkerboard<commit_after>use crate::noise_fns::NoiseFn;

/// Noise function that outputs a checkerboard pattern.
///
/// This noise function can take one input, size, and outputs 2size-sized
/// blocks of alternating values. The values of these blocks alternate between
/// -1.0 and 1.0.
///
/// This noise function is not very useful by itself, but it can be used for
/// debugging purposes.
#[derive(Clone, Copy, Debug)]
pub struct Checkerboard {
    // Controls the size of the block in 2^(size).
    size: usize,
}

impl Checkerboard {
    const DEFAULT_SIZE: usize = 0;

    /// Controls the size of the block in 2^(size) units.
    pub fn new(size: usize) -> Self {
        Self { size: 1 << size }
    }

    pub fn set_size(self, size: usize) -> Self {
        Self { size: 1 << size }
    }

    pub fn size(self) -> usize {
        self.size
    }
}

impl Default for Checkerboard {
    fn default() -> Self {
        Self {
            size: 1 << Checkerboard::DEFAULT_SIZE,
        }
    }
}

impl<const N: usize> NoiseFn<f64, N> for Checkerboard {
    fn get(&self, point: [f64; N]) -> f64 {
        let result = point
            .iter()
            .map(|&a| a.floor() as isize)
            .reduce(|a, b| (a & self.size as isize) ^ (b & self.size as isize))
            .unwrap();

        if result > 0 {
            -1.0
        } else {
            1.0
        }
    }
}
<|endoftext|>


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

extern crate env_logger;

use std::sync::{Once, ONCE_INIT};

static LOGGER_INIT: Once = ONCE_INIT;

/// Initialize the logger once.  More than one init() attempt returns
/// errors.
pub fn init_logger() {
    LOGGER_INIT.call_once(|| env_logger::init());
}
<commit_msg>Omit redundant closure<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate env_logger;

use std::sync::{Once, ONCE_INIT};

static LOGGER_INIT: Once = ONCE_INIT;

/// Initialize the logger once.  More than one init() attempt returns
/// errors.
pub fn init_logger() {
    LOGGER_INIT.call_once(env_logger::init);
}
<|endoftext|>


<commit_before>/// Errors produced by `Matcher::matches`
///
/// These correspond to `MatcherException` on the C++ side.
pub enum MatcherError {
    /// Current matchable doesn't have an attribute named `attr`
    AttributeUnavailable { attr: String },

    /// Compiling regular expression `regex` produced an error message `errmsg`
    InvalidRegex { regex: String, errmsg: String },
}
<commit_msg>End rustdocs with period<commit_after>/// Errors produced by `Matcher::matches`.
///
/// These correspond to `MatcherException` on the C++ side.
pub enum MatcherError {
    /// Current matchable doesn't have an attribute named `attr`
    AttributeUnavailable { attr: String },

    /// Compiling regular expression `regex` produced an error message `errmsg`
    InvalidRegex { regex: String, errmsg: String },
}
<|endoftext|>


<commit_before>/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Copy)]
pub struct Struct_zmq_msg_t {
    pub unnamed_field1: [::std::os::raw::c_uchar; 64usize],
}
impl ::std::clone::Clone for Struct_zmq_msg_t {
    fn clone(&self) -> Self { *self }
}
impl ::std::default::Default for Struct_zmq_msg_t {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}
pub type zmq_msg_t = Struct_zmq_msg_t;
pub type zmq_free_fn =
    unsafe extern "C" fn(data: *mut ::std::os::raw::c_void,
                         hint: *mut ::std::os::raw::c_void);
#[repr(C)]
#[derive(Copy)]
pub struct Struct_zmq_pollitem_t {
    pub socket: *mut ::std::os::raw::c_void,
    pub fd: ::std::os::raw::c_int,
    pub events: ::std::os::raw::c_short,
    pub revents: ::std::os::raw::c_short,
}
impl ::std::clone::Clone for Struct_zmq_pollitem_t {
    fn clone(&self) -> Self { *self }
}
impl ::std::default::Default for Struct_zmq_pollitem_t {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}
pub type zmq_pollitem_t = Struct_zmq_pollitem_t;
pub enum Struct_iovec { }
pub type zmq_thread_fn =
    unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void);
#[link(name = "zmq")]
extern "C" {
    pub fn zmq_errno() -> ::std::os::raw::c_int;
    pub fn zmq_strerror(errnum: ::std::os::raw::c_int)
     -> *const ::std::os::raw::c_char;
    pub fn zmq_version(major: *mut ::std::os::raw::c_int,
                       minor: *mut ::std::os::raw::c_int,
                       patch: *mut ::std::os::raw::c_int);
    pub fn zmq_ctx_new() -> *mut ::std::os::raw::c_void;
    pub fn zmq_ctx_term(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_shutdown(ctx_: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_set(context: *mut ::std::os::raw::c_void,
                       option: ::std::os::raw::c_int,
                       optval: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_get(context: *mut ::std::os::raw::c_void,
                       option: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_init(io_threads: ::std::os::raw::c_int)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_term(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_destroy(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_init(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_init_size(msg: *mut zmq_msg_t, size: size_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_init_data(msg: *mut zmq_msg_t,
                             data: *mut ::std::os::raw::c_void, size: size_t,
                             ffn: *mut zmq_free_fn,
                             hint: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_send(msg: *mut zmq_msg_t, s: *mut ::std::os::raw::c_void,
                        flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_recv(msg: *mut zmq_msg_t, s: *mut ::std::os::raw::c_void,
                        flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_close(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_move(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_copy(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_data(msg: *mut zmq_msg_t) -> *mut ::std::os::raw::c_void;
    pub fn zmq_msg_size(msg: *mut zmq_msg_t) -> size_t;
    pub fn zmq_msg_more(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_get(msg: *mut zmq_msg_t, property: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_set(msg: *mut zmq_msg_t, property: ::std::os::raw::c_int,
                       optval: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_gets(msg: *mut zmq_msg_t,
                        property: *const ::std::os::raw::c_char)
     -> *const ::std::os::raw::c_char;
    pub fn zmq_socket(arg1: *mut ::std::os::raw::c_void,
                      _type: ::std::os::raw::c_int)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_close(s: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int;
    pub fn zmq_setsockopt(s: *mut ::std::os::raw::c_void,
                          option: ::std::os::raw::c_int,
                          optval: *const ::std::os::raw::c_void,
                          optvallen: size_t) -> ::std::os::raw::c_int;
    pub fn zmq_getsockopt(s: *mut ::std::os::raw::c_void,
                          option: ::std::os::raw::c_int,
                          optval: *mut ::std::os::raw::c_void,
                          optvallen: *mut size_t) -> ::std::os::raw::c_int;
    pub fn zmq_bind(s: *mut ::std::os::raw::c_void,
                    addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_connect(s: *mut ::std::os::raw::c_void,
                       addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_unbind(s: *mut ::std::os::raw::c_void,
                      addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_disconnect(s: *mut ::std::os::raw::c_void,
                          addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_send(s: *mut ::std::os::raw::c_void,
                    buf: *const ::std::os::raw::c_void, len: size_t,
                    flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_send_const(s: *mut ::std::os::raw::c_void,
                          buf: *const ::std::os::raw::c_void, len: size_t,
                          flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_recv(s: *mut ::std::os::raw::c_void,
                    buf: *mut ::std::os::raw::c_void, len: size_t,
                    flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_socket_monitor(s: *mut ::std::os::raw::c_void,
                              addr: *const ::std::os::raw::c_char,
                              events: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_poll(items: *mut zmq_pollitem_t, nitems: ::std::os::raw::c_int,
                    timeout: ::std::os::raw::c_long) -> ::std::os::raw::c_int;
    pub fn zmq_proxy(frontend: *mut ::std::os::raw::c_void,
                     backend: *mut ::std::os::raw::c_void,
                     capture: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_proxy_steerable(frontend: *mut ::std::os::raw::c_void,
                               backend: *mut ::std::os::raw::c_void,
                               capture: *mut ::std::os::raw::c_void,
                               control: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_has(capability: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_device(_type: ::std::os::raw::c_int,
                      frontend: *mut ::std::os::raw::c_void,
                      backend: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_sendmsg(s: *mut ::std::os::raw::c_void, msg: *mut zmq_msg_t,
                       flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_recvmsg(s: *mut ::std::os::raw::c_void, msg: *mut zmq_msg_t,
                       flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_z85_encode(dest: *mut ::std::os::raw::c_char,
                          data: *const uint8_t, size: size_t)
     -> *mut ::std::os::raw::c_char;
    pub fn zmq_z85_decode(dest: *mut uint8_t,
                          string: *const ::std::os::raw::c_char)
     -> *mut uint8_t;
    pub fn zmq_curve_keypair(z85_public_key: *mut ::std::os::raw::c_char,
                             z85_secret_key: *mut ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_sendiov(s: *mut ::std::os::raw::c_void, iov: *mut Struct_iovec,
                       count: size_t, flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_recviov(s: *mut ::std::os::raw::c_void, iov: *mut Struct_iovec,
                       count: *mut size_t, flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_stopwatch_start() -> *mut ::std::os::raw::c_void;
    pub fn zmq_stopwatch_stop(watch_: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_ulong;
    pub fn zmq_sleep(seconds_: ::std::os::raw::c_int);
    pub fn zmq_threadstart(func: *mut zmq_thread_fn,
                           arg: *mut ::std::os::raw::c_void)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_threadclose(thread: *mut ::std::os::raw::c_void);
}
<commit_msg>Change zmq_msg_t struct in ffi to use an i64 to ensure correct alignment<commit_after>/* automatically generated by rust-bindgen */

#[repr(C)]
#[derive(Copy)]
pub struct Struct_zmq_msg_t {
    pub unnamed_field1: [i64; 8usize],
}
impl ::std::clone::Clone for Struct_zmq_msg_t {
    fn clone(&self) -> Self { *self }
}
impl ::std::default::Default for Struct_zmq_msg_t {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}
pub type zmq_msg_t = Struct_zmq_msg_t;
pub type zmq_free_fn =
    unsafe extern "C" fn(data: *mut ::std::os::raw::c_void,
                         hint: *mut ::std::os::raw::c_void);
#[repr(C)]
#[derive(Copy)]
pub struct Struct_zmq_pollitem_t {
    pub socket: *mut ::std::os::raw::c_void,
    pub fd: ::std::os::raw::c_int,
    pub events: ::std::os::raw::c_short,
    pub revents: ::std::os::raw::c_short,
}
impl ::std::clone::Clone for Struct_zmq_pollitem_t {
    fn clone(&self) -> Self { *self }
}
impl ::std::default::Default for Struct_zmq_pollitem_t {
    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
}
pub type zmq_pollitem_t = Struct_zmq_pollitem_t;
pub enum Struct_iovec { }
pub type zmq_thread_fn =
    unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void);
#[link(name = "zmq")]
extern "C" {
    pub fn zmq_errno() -> ::std::os::raw::c_int;
    pub fn zmq_strerror(errnum: ::std::os::raw::c_int)
     -> *const ::std::os::raw::c_char;
    pub fn zmq_version(major: *mut ::std::os::raw::c_int,
                       minor: *mut ::std::os::raw::c_int,
                       patch: *mut ::std::os::raw::c_int);
    pub fn zmq_ctx_new() -> *mut ::std::os::raw::c_void;
    pub fn zmq_ctx_term(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_shutdown(ctx_: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_set(context: *mut ::std::os::raw::c_void,
                       option: ::std::os::raw::c_int,
                       optval: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_get(context: *mut ::std::os::raw::c_void,
                       option: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_init(io_threads: ::std::os::raw::c_int)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_term(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_ctx_destroy(context: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_init(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_init_size(msg: *mut zmq_msg_t, size: size_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_init_data(msg: *mut zmq_msg_t,
                             data: *mut ::std::os::raw::c_void, size: size_t,
                             ffn: *mut zmq_free_fn,
                             hint: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_send(msg: *mut zmq_msg_t, s: *mut ::std::os::raw::c_void,
                        flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_recv(msg: *mut zmq_msg_t, s: *mut ::std::os::raw::c_void,
                        flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_close(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_move(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_copy(dest: *mut zmq_msg_t, src: *mut zmq_msg_t)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_data(msg: *mut zmq_msg_t) -> *mut ::std::os::raw::c_void;
    pub fn zmq_msg_size(msg: *mut zmq_msg_t) -> size_t;
    pub fn zmq_msg_more(msg: *mut zmq_msg_t) -> ::std::os::raw::c_int;
    pub fn zmq_msg_get(msg: *mut zmq_msg_t, property: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_set(msg: *mut zmq_msg_t, property: ::std::os::raw::c_int,
                       optval: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_msg_gets(msg: *mut zmq_msg_t,
                        property: *const ::std::os::raw::c_char)
     -> *const ::std::os::raw::c_char;
    pub fn zmq_socket(arg1: *mut ::std::os::raw::c_void,
                      _type: ::std::os::raw::c_int)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_close(s: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int;
    pub fn zmq_setsockopt(s: *mut ::std::os::raw::c_void,
                          option: ::std::os::raw::c_int,
                          optval: *const ::std::os::raw::c_void,
                          optvallen: size_t) -> ::std::os::raw::c_int;
    pub fn zmq_getsockopt(s: *mut ::std::os::raw::c_void,
                          option: ::std::os::raw::c_int,
                          optval: *mut ::std::os::raw::c_void,
                          optvallen: *mut size_t) -> ::std::os::raw::c_int;
    pub fn zmq_bind(s: *mut ::std::os::raw::c_void,
                    addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_connect(s: *mut ::std::os::raw::c_void,
                       addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_unbind(s: *mut ::std::os::raw::c_void,
                      addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_disconnect(s: *mut ::std::os::raw::c_void,
                          addr: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_send(s: *mut ::std::os::raw::c_void,
                    buf: *const ::std::os::raw::c_void, len: size_t,
                    flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_send_const(s: *mut ::std::os::raw::c_void,
                          buf: *const ::std::os::raw::c_void, len: size_t,
                          flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_recv(s: *mut ::std::os::raw::c_void,
                    buf: *mut ::std::os::raw::c_void, len: size_t,
                    flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_socket_monitor(s: *mut ::std::os::raw::c_void,
                              addr: *const ::std::os::raw::c_char,
                              events: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_poll(items: *mut zmq_pollitem_t, nitems: ::std::os::raw::c_int,
                    timeout: ::std::os::raw::c_long) -> ::std::os::raw::c_int;
    pub fn zmq_proxy(frontend: *mut ::std::os::raw::c_void,
                     backend: *mut ::std::os::raw::c_void,
                     capture: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_proxy_steerable(frontend: *mut ::std::os::raw::c_void,
                               backend: *mut ::std::os::raw::c_void,
                               capture: *mut ::std::os::raw::c_void,
                               control: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_has(capability: *const ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_device(_type: ::std::os::raw::c_int,
                      frontend: *mut ::std::os::raw::c_void,
                      backend: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_int;
    pub fn zmq_sendmsg(s: *mut ::std::os::raw::c_void, msg: *mut zmq_msg_t,
                       flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_recvmsg(s: *mut ::std::os::raw::c_void, msg: *mut zmq_msg_t,
                       flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
    pub fn zmq_z85_encode(dest: *mut ::std::os::raw::c_char,
                          data: *const uint8_t, size: size_t)
     -> *mut ::std::os::raw::c_char;
    pub fn zmq_z85_decode(dest: *mut uint8_t,
                          string: *const ::std::os::raw::c_char)
     -> *mut uint8_t;
    pub fn zmq_curve_keypair(z85_public_key: *mut ::std::os::raw::c_char,
                             z85_secret_key: *mut ::std::os::raw::c_char)
     -> ::std::os::raw::c_int;
    pub fn zmq_sendiov(s: *mut ::std::os::raw::c_void, iov: *mut Struct_iovec,
                       count: size_t, flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_recviov(s: *mut ::std::os::raw::c_void, iov: *mut Struct_iovec,
                       count: *mut size_t, flags: ::std::os::raw::c_int)
     -> ::std::os::raw::c_int;
    pub fn zmq_stopwatch_start() -> *mut ::std::os::raw::c_void;
    pub fn zmq_stopwatch_stop(watch_: *mut ::std::os::raw::c_void)
     -> ::std::os::raw::c_ulong;
    pub fn zmq_sleep(seconds_: ::std::os::raw::c_int);
    pub fn zmq_threadstart(func: *mut zmq_thread_fn,
                           arg: *mut ::std::os::raw::c_void)
     -> *mut ::std::os::raw::c_void;
    pub fn zmq_threadclose(thread: *mut ::std::os::raw::c_void);
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate log;
extern crate env_logger;

extern crate chrono;
extern crate hyper;
extern crate rs_es;
extern crate regex;
extern crate geo;
extern crate geojson;

pub mod objects;
pub mod rubber;
pub use objects::*;
use chrono::Local;

pub fn logger_init() -> Result<(), log::SetLoggerError> {
    let mut builder = env_logger::LogBuilder::new();

    builder.filter(None, log::LogLevelFilter::Info)
        .format(|record| {
            format!("[{time}]{lvl}:{loc}: {msg}",
                    time = Local::now(),
                    lvl = record.level(),
                    loc = record.location().module_path(),
                    msg = record.args())
        });
    if let Ok(s) = std::env::var("RUST_LOG") {
        builder.parse(&s);
    }
    builder.init()
}
<commit_msg>output datetime in log when LOG_TIME=1<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate log;
extern crate env_logger;

extern crate chrono;
extern crate hyper;
extern crate rs_es;
extern crate regex;
extern crate geo;
extern crate geojson;

pub mod objects;
pub mod rubber;
pub use objects::*;
use chrono::Local;
use std::env;

pub fn logger_init() -> Result<(), log::SetLoggerError> {
    let mut builder = env_logger::LogBuilder::new();

    if env::var("LOG_TIME").ok().map_or(false, |s| s == "1") {
        builder.format(|record| {
            format!("[{time}]{lvl}:{loc}: {msg}",
                    time = Local::now(),
                    lvl = record.level(),
                    loc = record.location().module_path(),
                    msg = record.args())
        });
    } else {
        builder.format(|record| {
            format!("{lvl}:{loc}: {msg}",
                    lvl = record.level(),
                    loc = record.location().module_path(),
                    msg = record.args())
        });
    }

    builder.filter(None, log::LogLevelFilter::Info);
    if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    }
    builder.init()
}
<|endoftext|>


<commit_before>use cliargsparser::CliArgsParser;
use dirs;
use gettextrs::gettext;
use libc;
use logger::{self, Level};
use std::fs::{self, DirBuilder};
use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
use utils;
use xdg;

pub const NEWSBOAT_SUBDIR_XDG: &str = "newsboat";
pub const NEWSBOAT_CONFIG_SUBDIR: &str = ".newsboat";
pub const NEWSBEUTER_SUBDIR_XDG: &str = "newsbeuter";
pub const NEWSBEUTER_CONFIG_SUBDIR: &str = ".newsbeuter";
pub const LOCK_SUFFIX: &str = ".lock";

const URLS_FILENAME: &str = "urls";
const CACHE_FILENAME: &str = "cache.db";
const CONFIG_FILENAME: &str = "config";
const QUEUE_FILENAME: &str = "queue";
const SEARCH_HISTORY_FILENAME: &str = "history.search";
const CMDLINE_HISTORY_FILENAME: &str = "history.cmdline";

#[derive(Debug, Default)]
pub struct ConfigPaths {
    /// Path to the user's home directory.
    ///
    /// Can be empty, in which case `error_message` should contain an explanation.
    env_home: PathBuf,

    /// An explanation why `env_home` is empty.
    error_message: String,

    /// Path to Newsboat's data directory.
    ///
    /// This can be ~/.newsboat, or ~/.local/share/newsboat, or something else entirely if user
    /// changed it through the command line parameter.
    data_dir: PathBuf,

    /// Path to Newsboat's configuration directory.
    ///
    /// This can be ~/.newsboat, or ~/.config/newsboat, or something else entirely if user changed
    /// it through the command line parameter.
    config_dir: PathBuf,

    url_file: PathBuf,
    cache_file: PathBuf,
    config_file: PathBuf,
    lock_file: PathBuf,
    queue_file: PathBuf,
    search_file: PathBuf,
    cmdline_file: PathBuf,

    silent: bool,
    using_nonstandard_configs: bool,
}

impl ConfigPaths {
    pub fn new() -> ConfigPaths {
        let mut config_paths = ConfigPaths {
            env_home: PathBuf::new(),
            error_message: String::new(),

            data_dir: PathBuf::new(),
            config_dir: PathBuf::new(),

            url_file: PathBuf::new(),
            cache_file: PathBuf::new(),
            config_file: PathBuf::new(),
            lock_file: PathBuf::new(),
            queue_file: PathBuf::new(),
            search_file: PathBuf::new(),
            cmdline_file: PathBuf::new(),

            silent: false,
            using_nonstandard_configs: false,
        };

        let env_home = dirs::home_dir();
        if env_home.is_none() {
            let uid = unsafe { libc::getuid() };

            config_paths.error_message = fmt!(
                &gettext(
                    "Fatal error: couldn't determine home directory!\n\
                     Please set the HOME environment variable or add \
                     a valid user for UID %u!"
                ),
                uid
            );

            return config_paths;
        }

        // hitting this branch means we found a home directory
        // we can now safely call unwrap on all other functions in the dirs crate

        config_paths.env_home = env_home.unwrap();
        config_paths.find_dirs();
        config_paths
    }

    fn migrate_data_from_newsbeuter_xdg(&mut self) -> bool {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let xdg_config_dir = xdg_dirs.get_config_home();
        let xdg_data_dir = xdg_dirs.get_data_home();

        let newsbeuter_config_dir = xdg_config_dir.join(NEWSBEUTER_SUBDIR_XDG);
        let newsbeuter_data_dir = xdg_data_dir.join(NEWSBEUTER_SUBDIR_XDG);

        let newsboat_config_dir = xdg_config_dir.join(NEWSBOAT_SUBDIR_XDG);
        let newsboat_data_dir = xdg_data_dir.join(NEWSBOAT_SUBDIR_XDG);

        if !newsbeuter_config_dir.is_dir() {
            return false;
        }

        fn exists(path: &Path) -> bool {
            let exists = path.exists();
            if exists {
                log!(
                    Level::Debug,
                    "{:?} already exists, aborting XDG migration.",
                    path
                );
            }
            exists
        }

        if exists(&newsboat_config_dir) {
            return false;
        }

        if exists(&newsboat_data_dir) {
            return false;
        }

        self.config_dir = newsboat_config_dir;
        self.data_dir = newsboat_data_dir;

        if !self.silent {
            eprintln!(
                "{}",
                &gettext("Migrating configs and data from Newsbeuter's XDG dirs...")
            );
        }

        if !try_mkdir(&self.config_dir) {
            return false;
        }

        if !try_mkdir(&self.data_dir) {
            return false;
        }

        // We ignore the return codes because it's okay if some files are missing.

        // in config
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, URLS_FILENAME);
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, CONFIG_FILENAME);

        // in data
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, CACHE_FILENAME);
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, QUEUE_FILENAME);
        let _ = migrate_file(
            &newsbeuter_data_dir,
            &self.data_dir,
            SEARCH_HISTORY_FILENAME,
        );
        let _ = migrate_file(
            &newsbeuter_data_dir,
            &self.data_dir,
            CMDLINE_HISTORY_FILENAME,
        );

        true
    }

    fn migrate_data_from_newsbeuter_simple(&self) -> bool {
        let newsbeuter_dir = self.env_home.join(NEWSBEUTER_CONFIG_SUBDIR);

        if !newsbeuter_dir.is_dir() {
            return false;
        }

        let newsboat_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        if newsboat_dir.exists() {
            log!(
                Level::Debug,
                "{:?} already exists, aborting migration.",
                newsboat_dir
            );
            return false;
        }

        if !self.silent {
            eprintln!(
                "{}",
                &gettext("Migrating configs and data from ~/.newsbeuter/...")
            );
        }

        match mkdir(&newsboat_dir, 0o700) {
            Ok(_) => (),
            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => (),
            Err(err) => {
                if !self.silent {
                    eprintln!(
                        "{}",
                        &fmt!(
                            &gettext("Aborting migration because mkdir on `%s' failed: %s"),
                            &newsboat_dir.to_string_lossy().into_owned(),
                            err.to_string()
                        )
                    );
                }
                return false;
            }
        };

        // We ignore the return codes because it's okay if some files are missing.
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, URLS_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CACHE_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CONFIG_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, QUEUE_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, SEARCH_HISTORY_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CMDLINE_HISTORY_FILENAME);

        true
    }

    fn migrate_data_from_newsbeuter(&mut self) -> bool {
        let mut migrated = self.migrate_data_from_newsbeuter_xdg();

        if migrated {
            // Re-running to pick up XDG dirs
            self.find_dirs();
        } else {
            migrated = self.migrate_data_from_newsbeuter_simple();
        }

        migrated
    }

    pub fn create_dirs(&self) -> bool {
        try_mkdir(&self.config_dir) && try_mkdir(&self.data_dir)
    }

    fn find_dirs(&mut self) {
        self.config_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        self.data_dir = self.config_dir.clone();

        // Will change config_dir and data_dir to point to XDG if XDG
        // directories are available.
        self.find_dirs_xdg();

        // in config
        self.url_file = self.config_dir.join(URLS_FILENAME);
        self.config_file = self.config_dir.join(CONFIG_FILENAME);

        // in data
        self.cache_file = self.data_dir.join(CACHE_FILENAME);
        self.lock_file = self.data_dir.join(CACHE_FILENAME.to_owned() + LOCK_SUFFIX);
        self.queue_file = self.data_dir.join(QUEUE_FILENAME);
        self.search_file = self.data_dir.join(SEARCH_HISTORY_FILENAME);
        self.cmdline_file = self.data_dir.join(CMDLINE_HISTORY_FILENAME);
    }

    fn find_dirs_xdg(&mut self) {
        // This can't panic because we've tested we can find the home directory in ConfigPaths::new
        // This should be replaced with proper error handling after this is not used by c++ anymore
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        let config_dir = xdg_dirs.get_config_home().join(NEWSBOAT_SUBDIR_XDG);
        let data_dir = xdg_dirs.get_data_home().join(NEWSBOAT_SUBDIR_XDG);

        if !config_dir.is_dir() {
            return;
        }

        /* Invariant: config dir exists.
         *
         * At this point, we're confident we'll be using XDG. We don't check if
         * data dir exists, because if it doesn't we'll create it. */

        self.config_dir = config_dir;
        self.data_dir = data_dir;
    }

    /// Indicates if the object can be used.
    ///
    /// If this method returned `false`, the cause for initialization failure can be found using
    /// `error_message()`.
    pub fn initialized(&self) -> bool {
        !self.env_home.as_os_str().is_empty()
    }

    /// Returns explanation why initialization failed.
    ///
    /// \note You shouldn't call this unless `initialized()` returns `false`.
    pub fn error_message(&self) -> &str {
        &self.error_message
    }

    /// Initializes paths to config, cache etc. from CLI arguments.
    pub fn process_args(&mut self, args: &CliArgsParser) {
        if let Some(ref url_file) = args.url_file {
            self.url_file = url_file.to_owned();
        }

        if let Some(ref cache_file) = args.cache_file {
            self.cache_file = cache_file.to_owned();
        }

        if let Some(ref lock_file) = args.lock_file {
            self.lock_file = lock_file.to_owned();
        }

        if let Some(ref config_file) = args.config_file {
            self.config_file = config_file.to_owned();
        }

        self.silent = args.silent;
        self.using_nonstandard_configs = args.using_nonstandard_configs();
    }

    /// Migrate configs and data from Newsbeuter if they exist. Return `true` if migrated
    /// something, `false` otherwise.
    pub fn try_migrate_from_newsbeuter(&mut self) -> bool {
        if !self.using_nonstandard_configs && !&self.url_file.exists() {
            return self.migrate_data_from_newsbeuter();
        }

        // No migration occurred.
        false
    }

    /// Path to the URLs file.
    pub fn url_file(&self) -> &Path {
        &self.url_file
    }

    /// Path to the cache file.
    pub fn cache_file(&self) -> &Path {
        &self.cache_file
    }

    /// Sets path to the cache file.
    // FIXME: this is actually a kludge that lets Controller change the path
    // midway. That logic should be moved into ConfigPaths, and this method
    // removed.
    pub fn set_cache_file(&mut self, mut path: PathBuf) {
        self.cache_file = path.clone();
        self.lock_file = {
            let current_extension = path
                .extension()
                .map(|p| p.to_string_lossy().into_owned())
                .unwrap_or_else(String::new);
            path.set_extension(current_extension + LOCK_SUFFIX);
            path
        };
    }

    /// Path to the config file.
    pub fn config_file(&self) -> &Path {
        &self.config_file
    }

    /// Path to the lock file.
    ///
    /// \note This changes when path to config file changes.
    pub fn lock_file(&self) -> &Path {
        &self.lock_file
    }

    /// \brief Path to the queue file.
    ///
    /// Queue file stores enqueued podcasts. It's written by Newsboat, and read by Podboat.
    pub fn queue_file(&self) -> &Path {
        &self.queue_file
    }

    /// Path to the file with previous search queries.
    pub fn search_file(&self) -> &Path {
        &self.search_file
    }

    /// Path to the file with command-line history.
    pub fn cmdline_file(&self) -> &Path {
        &self.cmdline_file
    }
}

fn try_mkdir<R: AsRef<Path>>(path: R) -> bool {
    utils::mkdir_parents(&path.as_ref(), 0o700).is_ok()
}

fn mkdir<R: AsRef<Path>>(path: R, mode: u32) -> io::Result<()> {
    DirBuilder::new().mode(mode).create(path.as_ref())
}

fn migrate_file<R: AsRef<Path>>(newsbeuter_dir: R, newsboat_dir: R, file: &str) -> io::Result<()> {
    let input_filepath = newsbeuter_dir.as_ref().join(file);
    let output_filepath = newsboat_dir.as_ref().join(file);
    eprintln!("{:?} -> {:?}", input_filepath, output_filepath);
    fs::copy(input_filepath, output_filepath).map(|_| ())
}
<commit_msg>ConfigPaths: figure out XDG locations only once<commit_after>use cliargsparser::CliArgsParser;
use dirs;
use gettextrs::gettext;
use libc;
use logger::{self, Level};
use std::fs::{self, DirBuilder};
use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
use utils;
use xdg;

pub const NEWSBOAT_SUBDIR_XDG: &str = "newsboat";
pub const NEWSBOAT_CONFIG_SUBDIR: &str = ".newsboat";
pub const NEWSBEUTER_SUBDIR_XDG: &str = "newsbeuter";
pub const NEWSBEUTER_CONFIG_SUBDIR: &str = ".newsbeuter";
pub const LOCK_SUFFIX: &str = ".lock";

const URLS_FILENAME: &str = "urls";
const CACHE_FILENAME: &str = "cache.db";
const CONFIG_FILENAME: &str = "config";
const QUEUE_FILENAME: &str = "queue";
const SEARCH_HISTORY_FILENAME: &str = "history.search";
const CMDLINE_HISTORY_FILENAME: &str = "history.cmdline";

#[derive(Debug, Default)]
pub struct ConfigPaths {
    /// Path to the user's home directory.
    ///
    /// Can be empty, in which case `error_message` should contain an explanation.
    env_home: PathBuf,

    /// An explanation why `env_home` is empty.
    error_message: String,

    /// Path to user's XDG config home.
    ///
    /// This is the value of the XDG_CONFIG_HOME environment variable, otherwise ~/.config (with
    /// tilde resolved).
    config_home: PathBuf,

    /// Path to user's XDG data home.
    ///
    /// This is the value of the XDG_DATA_HOME environment variable, otherwise ~/.local/share (with
    /// tilde resolved).
    data_home: PathBuf,

    /// Path to Newsboat's data directory.
    ///
    /// This can be ~/.newsboat, or ~/.local/share/newsboat, or something else entirely if the user
    /// changed it through the command line parameter.
    data_dir: PathBuf,

    /// Path to Newsboat's configuration directory.
    ///
    /// This can be ~/.newsboat, or ~/.config/newsboat, or something else entirely if the user
    /// changed it through the command line parameter.
    config_dir: PathBuf,

    url_file: PathBuf,
    cache_file: PathBuf,
    config_file: PathBuf,
    lock_file: PathBuf,
    queue_file: PathBuf,
    search_file: PathBuf,
    cmdline_file: PathBuf,

    silent: bool,
    using_nonstandard_configs: bool,
}

impl ConfigPaths {
    pub fn new() -> ConfigPaths {
        let mut config_paths = ConfigPaths {
            env_home: PathBuf::new(),
            error_message: String::new(),

            config_home: PathBuf::new(),
            data_home: PathBuf::new(),

            data_dir: PathBuf::new(),
            config_dir: PathBuf::new(),

            url_file: PathBuf::new(),
            cache_file: PathBuf::new(),
            config_file: PathBuf::new(),
            lock_file: PathBuf::new(),
            queue_file: PathBuf::new(),
            search_file: PathBuf::new(),
            cmdline_file: PathBuf::new(),

            silent: false,
            using_nonstandard_configs: false,
        };

        let env_home = dirs::home_dir();
        if env_home.is_none() {
            let uid = unsafe { libc::getuid() };

            config_paths.error_message = fmt!(
                &gettext(
                    "Fatal error: couldn't determine home directory!\n\
                     Please set the HOME environment variable or add \
                     a valid user for UID %u!"
                ),
                uid
            );

            return config_paths;
        }

        // hitting this branch means we found a home directory
        // we can now safely call unwrap on all other functions in the dirs crate
        config_paths.env_home = env_home.unwrap();

        // This can't panic because we've found the home dir.
        // This should be replaced with proper error handling after this is not used by C++
        // anymore.
        let xdg_dirs = xdg::BaseDirectories::new().unwrap();
        config_paths.config_home = xdg_dirs.get_config_home();
        config_paths.data_home = xdg_dirs.get_data_home();

        config_paths.find_dirs();
        config_paths
    }

    fn migrate_data_from_newsbeuter_xdg(&mut self) -> bool {
        let newsbeuter_config_dir = self.config_home.join(NEWSBEUTER_SUBDIR_XDG);
        let newsbeuter_data_dir = self.data_home.join(NEWSBEUTER_SUBDIR_XDG);

        let newsboat_config_dir = self.config_home.join(NEWSBOAT_SUBDIR_XDG);
        let newsboat_data_dir = self.data_home.join(NEWSBOAT_SUBDIR_XDG);

        if !newsbeuter_config_dir.is_dir() {
            return false;
        }

        fn exists(path: &Path) -> bool {
            let exists = path.exists();
            if exists {
                log!(
                    Level::Debug,
                    "{:?} already exists, aborting XDG migration.",
                    path
                );
            }
            exists
        }

        if exists(&newsboat_config_dir) {
            return false;
        }

        if exists(&newsboat_data_dir) {
            return false;
        }

        self.config_dir = newsboat_config_dir;
        self.data_dir = newsboat_data_dir;

        if !self.silent {
            eprintln!(
                "{}",
                &gettext("Migrating configs and data from Newsbeuter's XDG dirs...")
            );
        }

        if !try_mkdir(&self.config_dir) {
            return false;
        }

        if !try_mkdir(&self.data_dir) {
            return false;
        }

        // We ignore the return codes because it's okay if some files are missing.

        // in config
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, URLS_FILENAME);
        let _ = migrate_file(&newsbeuter_config_dir, &self.config_dir, CONFIG_FILENAME);

        // in data
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, CACHE_FILENAME);
        let _ = migrate_file(&newsbeuter_data_dir, &self.data_dir, QUEUE_FILENAME);
        let _ = migrate_file(
            &newsbeuter_data_dir,
            &self.data_dir,
            SEARCH_HISTORY_FILENAME,
        );
        let _ = migrate_file(
            &newsbeuter_data_dir,
            &self.data_dir,
            CMDLINE_HISTORY_FILENAME,
        );

        true
    }

    fn migrate_data_from_newsbeuter_simple(&self) -> bool {
        let newsbeuter_dir = self.env_home.join(NEWSBEUTER_CONFIG_SUBDIR);

        if !newsbeuter_dir.is_dir() {
            return false;
        }

        let newsboat_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        if newsboat_dir.exists() {
            log!(
                Level::Debug,
                "{:?} already exists, aborting migration.",
                newsboat_dir
            );
            return false;
        }

        if !self.silent {
            eprintln!(
                "{}",
                &gettext("Migrating configs and data from ~/.newsbeuter/...")
            );
        }

        match mkdir(&newsboat_dir, 0o700) {
            Ok(_) => (),
            Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => (),
            Err(err) => {
                if !self.silent {
                    eprintln!(
                        "{}",
                        &fmt!(
                            &gettext("Aborting migration because mkdir on `%s' failed: %s"),
                            &newsboat_dir.to_string_lossy().into_owned(),
                            err.to_string()
                        )
                    );
                }
                return false;
            }
        };

        // We ignore the return codes because it's okay if some files are missing.
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, URLS_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CACHE_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CONFIG_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, QUEUE_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, SEARCH_HISTORY_FILENAME);
        let _ = migrate_file(&newsbeuter_dir, &newsboat_dir, CMDLINE_HISTORY_FILENAME);

        true
    }

    fn migrate_data_from_newsbeuter(&mut self) -> bool {
        let mut migrated = self.migrate_data_from_newsbeuter_xdg();

        if migrated {
            // Re-running to pick up XDG dirs
            self.find_dirs();
        } else {
            migrated = self.migrate_data_from_newsbeuter_simple();
        }

        migrated
    }

    pub fn create_dirs(&self) -> bool {
        try_mkdir(&self.config_dir) && try_mkdir(&self.data_dir)
    }

    fn find_dirs(&mut self) {
        self.config_dir = self.env_home.join(NEWSBOAT_CONFIG_SUBDIR);

        self.data_dir = self.config_dir.clone();

        // Will change config_dir and data_dir to point to XDG if XDG
        // directories are available.
        self.find_dirs_xdg();

        // in config
        self.url_file = self.config_dir.join(URLS_FILENAME);
        self.config_file = self.config_dir.join(CONFIG_FILENAME);

        // in data
        self.cache_file = self.data_dir.join(CACHE_FILENAME);
        self.lock_file = self.data_dir.join(CACHE_FILENAME.to_owned() + LOCK_SUFFIX);
        self.queue_file = self.data_dir.join(QUEUE_FILENAME);
        self.search_file = self.data_dir.join(SEARCH_HISTORY_FILENAME);
        self.cmdline_file = self.data_dir.join(CMDLINE_HISTORY_FILENAME);
    }

    fn find_dirs_xdg(&mut self) {
        let config_dir = self.config_home.join(NEWSBOAT_SUBDIR_XDG);
        let data_dir = self.data_home.join(NEWSBOAT_SUBDIR_XDG);

        if !config_dir.is_dir() {
            return;
        }

        /* Invariant: config dir exists.
         *
         * At this point, we're confident we'll be using XDG. We don't check if
         * data dir exists, because if it doesn't we'll create it. */

        self.config_dir = config_dir;
        self.data_dir = data_dir;
    }

    /// Indicates if the object can be used.
    ///
    /// If this method returned `false`, the cause for initialization failure can be found using
    /// `error_message()`.
    pub fn initialized(&self) -> bool {
        !self.env_home.as_os_str().is_empty()
    }

    /// Returns explanation why initialization failed.
    ///
    /// \note You shouldn't call this unless `initialized()` returns `false`.
    pub fn error_message(&self) -> &str {
        &self.error_message
    }

    /// Initializes paths to config, cache etc. from CLI arguments.
    pub fn process_args(&mut self, args: &CliArgsParser) {
        if let Some(ref url_file) = args.url_file {
            self.url_file = url_file.to_owned();
        }

        if let Some(ref cache_file) = args.cache_file {
            self.cache_file = cache_file.to_owned();
        }

        if let Some(ref lock_file) = args.lock_file {
            self.lock_file = lock_file.to_owned();
        }

        if let Some(ref config_file) = args.config_file {
            self.config_file = config_file.to_owned();
        }

        self.silent = args.silent;
        self.using_nonstandard_configs = args.using_nonstandard_configs();
    }

    /// Migrate configs and data from Newsbeuter if they exist. Return `true` if migrated
    /// something, `false` otherwise.
    pub fn try_migrate_from_newsbeuter(&mut self) -> bool {
        if !self.using_nonstandard_configs && !&self.url_file.exists() {
            return self.migrate_data_from_newsbeuter();
        }

        // No migration occurred.
        false
    }

    /// Path to the URLs file.
    pub fn url_file(&self) -> &Path {
        &self.url_file
    }

    /// Path to the cache file.
    pub fn cache_file(&self) -> &Path {
        &self.cache_file
    }

    /// Sets path to the cache file.
    // FIXME: this is actually a kludge that lets Controller change the path
    // midway. That logic should be moved into ConfigPaths, and this method
    // removed.
    pub fn set_cache_file(&mut self, mut path: PathBuf) {
        self.cache_file = path.clone();
        self.lock_file = {
            let current_extension = path
                .extension()
                .map(|p| p.to_string_lossy().into_owned())
                .unwrap_or_else(String::new);
            path.set_extension(current_extension + LOCK_SUFFIX);
            path
        };
    }

    /// Path to the config file.
    pub fn config_file(&self) -> &Path {
        &self.config_file
    }

    /// Path to the lock file.
    ///
    /// \note This changes when path to config file changes.
    pub fn lock_file(&self) -> &Path {
        &self.lock_file
    }

    /// \brief Path to the queue file.
    ///
    /// Queue file stores enqueued podcasts. It's written by Newsboat, and read by Podboat.
    pub fn queue_file(&self) -> &Path {
        &self.queue_file
    }

    /// Path to the file with previous search queries.
    pub fn search_file(&self) -> &Path {
        &self.search_file
    }

    /// Path to the file with command-line history.
    pub fn cmdline_file(&self) -> &Path {
        &self.cmdline_file
    }
}

fn try_mkdir<R: AsRef<Path>>(path: R) -> bool {
    utils::mkdir_parents(&path.as_ref(), 0o700).is_ok()
}

fn mkdir<R: AsRef<Path>>(path: R, mode: u32) -> io::Result<()> {
    DirBuilder::new().mode(mode).create(path.as_ref())
}

fn migrate_file<R: AsRef<Path>>(newsbeuter_dir: R, newsboat_dir: R, file: &str) -> io::Result<()> {
    let input_filepath = newsbeuter_dir.as_ref().join(file);
    let output_filepath = newsboat_dir.as_ref().join(file);
    eprintln!("{:?} -> {:?}", input_filepath, output_filepath);
    fs::copy(input_filepath, output_filepath).map(|_| ())
}
<|endoftext|>


<commit_before>use std::mem;
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
use syntax::ext::quote::rt::ToTokens;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::parse;
use syntax::parse::parser::Parser as RustParser;
use syntax::parse::token::{self, DelimToken};
use syntax::ptr::P;

use super::render::{Escape, Renderer};

macro_rules! dollar {
    () => (TtToken(_, token::Dollar))
}
macro_rules! dot {
    () => (TtToken(_, token::Dot))
}
macro_rules! eq {
    () => (TtToken(_, token::Eq))
}
macro_rules! not {
    () => (TtToken(_, token::Not))
}
macro_rules! question {
    () => (TtToken(_, token::Question))
}
macro_rules! semi {
    () => (TtToken(_, token::Semi))
}
macro_rules! minus {
    () => (TtToken(_, token::BinOp(token::Minus)))
}
macro_rules! slash {
    () => (TtToken(_, token::BinOp(token::Slash)))
}
macro_rules! literal {
    () => (TtToken(_, token::Literal(..)))
}
macro_rules! ident {
    ($x:pat) => (ident!(_, $x));
    ($sp:pat, $x:pat) => (TtToken($sp, token::Ident($x, token::IdentStyle::Plain)))
}

pub fn parse(cx: &ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> {
    let mut parser = Parser {
        in_attr: false,
        input: input,
        span: sp,
        render: Renderer::new(cx),
    };
    parser.markups();
    parser.into_render().into_expr()
}

struct Parser<'cx, 's: 'cx, 'i> {
    in_attr: bool,
    input: &'i [TokenTree],
    span: Span,
    render: Renderer<'cx, 's>,
}

impl<'cx, 's, 'i> Parser<'cx, 's, 'i> {
    /// Finalize the `Parser`, returning the `Renderer` underneath.
    fn into_render(self) -> Renderer<'cx, 's> {
        let Parser { render, .. } = self;
        render
    }

    /// Consume `n` items from the input.
    fn shift(&mut self, n: usize) {
        self.input = &self.input[n..];
    }

    /// Construct a Rust AST parser from the given token tree.
    fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> {
        parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone())
    }

    fn markups(&mut self) {
        loop {
            match self.input {
                [] => return,
                [semi!(), ..] => self.shift(1),
                [_, ..] => if !self.markup() { return },
            }
        }
    }

    fn markup(&mut self) -> bool {
        match self.input {
            // Literal
            [minus!(), ref tt @ literal!(), ..] => {
                self.shift(2);
                self.literal(tt, true)
            },
            [ref tt @ literal!(), ..] => {
                self.shift(1);
                self.literal(tt, false)
            },
            // If
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "if" => {
                self.shift(2);
                self.if_expr(sp);
            },
            // For
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "for" => {
                self.shift(2);
                self.for_expr(sp);
            },
            // Splice
            [ref tt @ dollar!(), dollar!(), ..] => {
                self.shift(2);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::PassThru);
            },
            [ref tt @ dollar!(), ..] => {
                self.shift(1);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::Escape);
            },
            // Element
            [ident!(sp, name), ..] => {
                self.shift(1);
                self.element(sp, name.as_str())
            },
            // Block
            [TtDelimited(sp, ref d), ..] if d.delim == token::DelimToken::Brace => {
                self.shift(1);
                let stmts = self.block(sp, &d.tts);
                self.render.push_stmts(stmts);
            },
            // ???
            _ => {
                if let [ref tt, ..] = self.input {
                    self.render.cx.span_err(tt.get_span(), "invalid syntax");
                } else {
                    self.render.cx.span_err(self.span, "unexpected end of block");
                }
                return false;
            },
        }
        true
    }

    fn literal(&mut self, tt: &TokenTree, minus: bool) {
        let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit();
        match lit_to_string(self.render.cx, lit, minus) {
            Some(s) => self.render.string(&s, Escape::Escape),
            None => {},
        }
    }

    fn if_expr(&mut self, sp: Span) {
        // Parse the initial if
        let mut if_cond = vec![];
        let if_body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                if_body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                if_cond.push(tt.clone());
            },
            [] => self.render.cx.span_fatal(sp, "expected body for this $if"),
        }}
        // Parse the (optional) else
        let else_body = match self.input {
            [dollar!(), ident!(else_), ..] if else_.as_str() == "else" => {
                self.shift(2);
                match self.input {
                    [ident!(sp, if_), ..] if if_.as_str() == "if" => {
                        self.shift(1);
                        let else_body = {
                            // Parse an if expression, but capture the result
                            // rather than emitting it right away
                            let mut render = self.render.fork();
                            mem::swap(&mut self.render, &mut render);
                            self.if_expr(sp);
                            mem::swap(&mut self.render, &mut render);
                            render.into_stmts()
                        };
                        Some(else_body)
                    },
                    [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                        self.shift(1);
                        Some(self.block(sp, &d.tts))
                    },
                    _ => self.render.cx.span_fatal(sp, "expected body for this $else"),
                }
            },
            _ => None,
        };
        self.render.emit_if(if_cond, if_body, else_body);
    }

    fn for_expr(&mut self, sp: Span) {
        let mut pattern = vec![];
        loop { match self.input {
            [ident!(in_), ..] if in_.as_str() == "in" => {
                self.shift(1);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                pattern.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let pattern = self.new_rust_parser(pattern).parse_pat();
        let mut iterable = vec![];
        let body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                iterable.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let iterable = self.new_rust_parser(iterable).parse_expr();
        self.render.emit_for(pattern, iterable, body);
    }

    fn splice(&mut self, sp: Span) -> P<Expr> {
        let mut tts = vec![];
        // First, munch a single token tree
        if let [ref tt, ..] = self.input {
            self.shift(1);
            tts.push(tt.clone());
        }
        loop {
            match self.input {
                // Munch attribute lookups e.g. `$person.address.street`
                [ref dot @ dot!(), ref ident @ ident!(_), ..] => {
                    self.shift(2);
                    tts.push(dot.clone());
                    tts.push(ident.clone());
                },
                // Munch function calls `()` and indexing operations `[]`
                [TtDelimited(sp, ref d), ..] if d.delim != token::DelimToken::Brace => {
                    self.shift(1);
                    tts.push(TtDelimited(sp, d.clone()));
                },
                _ => break,
            }
        }
        if tts.is_empty() {
            self.render.cx.span_fatal(sp, "expected expression for this splice");
        } else {
            self.new_rust_parser(tts).parse_expr()
        }
    }

    fn element(&mut self, sp: Span, name: &str) {
        if self.in_attr {
            self.render.cx.span_err(sp, "unexpected element, you silly bumpkin");
            return;
        }
        self.render.element_open_start(name);
        self.attrs();
        self.render.element_open_end();
        if let [slash!(), ..] = self.input {
            self.shift(1);
        } else {
            self.markup();
            self.render.element_close(name);
        }
    }

    fn attrs(&mut self) {
        loop { match self.input {
            [ident!(name), eq!(), ..] => {
                // Non-empty attribute
                self.shift(2);
                self.render.attribute_start(name.as_str());
                {
                    // Parse a value under an attribute context
                    let mut in_attr = true;
                    mem::swap(&mut self.in_attr, &mut in_attr);
                    self.markup();
                    mem::swap(&mut self.in_attr, &mut in_attr);
                }
                self.render.attribute_end();
            },
            [ident!(name), question!(), ..] => {
                // Empty attribute
                self.shift(2);
                if let [ref tt @ eq!(), ..] = self.input {
                    // Toggle the attribute based on a boolean expression
                    self.shift(1);
                    let cond = self.splice(tt.get_span());
                    // Silence "unnecessary parentheses" warnings
                    let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
                    let body = {
                        let mut r = self.render.fork();
                        r.attribute_empty(name.as_str());
                        r.into_stmts()
                    };
                    self.render.emit_if(cond, body, None);
                } else {
                    // Write the attribute unconditionally
                    self.render.attribute_empty(name.as_str());
                }
            },
            _ => return,
        }}
    }

    fn block(&mut self, sp: Span, tts: &[TokenTree]) -> Vec<P<Stmt>> {
        let mut parse = Parser {
            in_attr: self.in_attr,
            input: tts,
            span: sp,
            render: self.render.fork(),
        };
        parse.markups();
        parse.into_render().into_stmts()
    }
}

/// Convert a literal to a string.
fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> {
    use syntax::ast::Lit_::*;
    let mut result = String::new();
    if minus {
        result.push('-');
    }
    match lit.node {
        LitStr(s, _) => result.push_str(&s),
        LitBinary(..) | LitByte(..) => {
            cx.span_err(lit.span, "cannot splice binary data");
            return None;
        },
        LitChar(c) => result.push(c),
        LitInt(x, _) => result.push_str(&x.to_string()),
        LitFloat(s, _) | LitFloatUnsuffixed(s) => result.push_str(&s),
        LitBool(b) => result.push_str(if b { "true" } else { "false" }),
    };
    Some(result)
}

/// If the expression is wrapped in parentheses, strip them off.
fn strip_outer_parens(expr: P<Expr>) -> P<Expr> {
    expr.and_then(|expr| match expr {
        Expr { node: ExprParen(inner), .. } => inner,
        expr => P(expr),
    })
}
<commit_msg>Cleanup<commit_after>use std::mem;
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, TtDelimited, TtToken};
use syntax::ext::quote::rt::ToTokens;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::parse;
use syntax::parse::parser::Parser as RustParser;
use syntax::parse::token::{self, DelimToken};
use syntax::ptr::P;

use super::render::{Escape, Renderer};

macro_rules! dollar {
    () => (TtToken(_, token::Dollar))
}
macro_rules! dot {
    () => (TtToken(_, token::Dot))
}
macro_rules! eq {
    () => (TtToken(_, token::Eq))
}
macro_rules! not {
    () => (TtToken(_, token::Not))
}
macro_rules! question {
    () => (TtToken(_, token::Question))
}
macro_rules! semi {
    () => (TtToken(_, token::Semi))
}
macro_rules! minus {
    () => (TtToken(_, token::BinOp(token::Minus)))
}
macro_rules! slash {
    () => (TtToken(_, token::BinOp(token::Slash)))
}
macro_rules! literal {
    () => (TtToken(_, token::Literal(..)))
}
macro_rules! ident {
    ($x:pat) => (ident!(_, $x));
    ($sp:pat, $x:pat) => (TtToken($sp, token::Ident($x, token::IdentStyle::Plain)))
}

pub fn parse(cx: &ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> {
    let mut parser = Parser {
        in_attr: false,
        input: input,
        span: sp,
        render: Renderer::new(cx),
    };
    parser.markups();
    parser.into_render().into_expr()
}

struct Parser<'cx, 's: 'cx, 'i> {
    in_attr: bool,
    input: &'i [TokenTree],
    span: Span,
    render: Renderer<'cx, 's>,
}

impl<'cx, 's, 'i> Parser<'cx, 's, 'i> {
    /// Finalize the `Parser`, returning the `Renderer` underneath.
    fn into_render(self) -> Renderer<'cx, 's> {
        let Parser { render, .. } = self;
        render
    }

    /// Consume `n` items from the input.
    fn shift(&mut self, n: usize) {
        self.input = &self.input[n..];
    }

    /// Construct a Rust AST parser from the given token tree.
    fn new_rust_parser(&self, tts: Vec<TokenTree>) -> RustParser<'s> {
        parse::tts_to_parser(self.render.cx.parse_sess, tts, self.render.cx.cfg.clone())
    }

    fn markups(&mut self) {
        loop {
            match self.input {
                [] => return,
                [semi!(), ..] => self.shift(1),
                [_, ..] => if !self.markup() { return },
            }
        }
    }

    fn markup(&mut self) -> bool {
        match self.input {
            // Literal
            [minus!(), ref tt @ literal!(), ..] => {
                self.shift(2);
                self.literal(tt, true)
            },
            [ref tt @ literal!(), ..] => {
                self.shift(1);
                self.literal(tt, false)
            },
            // If
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "if" => {
                self.shift(2);
                self.if_expr(sp);
            },
            // For
            [dollar!(), ident!(sp, name), ..] if name.as_str() == "for" => {
                self.shift(2);
                self.for_expr(sp);
            },
            // Splice
            [ref tt @ dollar!(), dollar!(), ..] => {
                self.shift(2);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::PassThru);
            },
            [ref tt @ dollar!(), ..] => {
                self.shift(1);
                let expr = self.splice(tt.get_span());
                self.render.splice(expr, Escape::Escape);
            },
            // Element
            [ident!(sp, name), ..] => {
                self.shift(1);
                self.element(sp, name.as_str())
            },
            // Block
            [TtDelimited(sp, ref d), ..] if d.delim == token::DelimToken::Brace => {
                self.shift(1);
                let stmts = self.block(sp, &d.tts);
                self.render.push_stmts(stmts);
            },
            // ???
            _ => {
                if let [ref tt, ..] = self.input {
                    self.render.cx.span_err(tt.get_span(), "invalid syntax");
                } else {
                    self.render.cx.span_err(self.span, "unexpected end of block");
                }
                return false;
            },
        }
        true
    }

    fn literal(&mut self, tt: &TokenTree, minus: bool) {
        let lit = self.new_rust_parser(vec![tt.clone()]).parse_lit();
        match lit_to_string(self.render.cx, lit, minus) {
            Some(s) => self.render.string(&s, Escape::Escape),
            None => {},
        }
    }

    fn if_expr(&mut self, sp: Span) {
        // Parse the initial if
        let mut if_cond = vec![];
        let if_body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                if_body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                if_cond.push(tt.clone());
            },
            [] => self.render.cx.span_fatal(sp, "expected body for this $if"),
        }}
        // Parse the (optional) else
        let else_body = match self.input {
            [dollar!(), ident!(else_), ..] if else_.as_str() == "else" => {
                self.shift(2);
                match self.input {
                    [ident!(sp, if_), ..] if if_.as_str() == "if" => {
                        self.shift(1);
                        let else_body = {
                            // Parse an if expression, but capture the result
                            // rather than emitting it right away
                            let mut render = self.render.fork();
                            mem::swap(&mut self.render, &mut render);
                            self.if_expr(sp);
                            mem::swap(&mut self.render, &mut render);
                            render.into_stmts()
                        };
                        Some(else_body)
                    },
                    [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                        self.shift(1);
                        Some(self.block(sp, &d.tts))
                    },
                    _ => self.render.cx.span_fatal(sp, "expected body for this $else"),
                }
            },
            _ => None,
        };
        self.render.emit_if(if_cond, if_body, else_body);
    }

    fn for_expr(&mut self, sp: Span) {
        let mut pattern = vec![];
        loop { match self.input {
            [ident!(in_), ..] if in_.as_str() == "in" => {
                self.shift(1);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                pattern.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let pattern = self.new_rust_parser(pattern).parse_pat();
        let mut iterable = vec![];
        let body;
        loop { match self.input {
            [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
                self.shift(1);
                body = self.block(sp, &d.tts);
                break;
            },
            [ref tt, ..] => {
                self.shift(1);
                iterable.push(tt.clone());
            },
            _ => self.render.cx.span_fatal(sp, "invalid $for"),
        }}
        let iterable = self.new_rust_parser(iterable).parse_expr();
        self.render.emit_for(pattern, iterable, body);
    }

    fn splice(&mut self, sp: Span) -> P<Expr> {
        let mut tts = vec![];
        // First, munch a single token tree
        if let [ref tt, ..] = self.input {
            self.shift(1);
            tts.push(tt.clone());
        }
        loop { match self.input {
            // Munch attribute lookups e.g. `$person.address.street`
            [ref dot @ dot!(), ref ident @ ident!(_), ..] => {
                self.shift(2);
                tts.push(dot.clone());
                tts.push(ident.clone());
            },
            // Munch function calls `()` and indexing operations `[]`
            [TtDelimited(sp, ref d), ..] if d.delim != token::DelimToken::Brace => {
                self.shift(1);
                tts.push(TtDelimited(sp, d.clone()));
            },
            _ => break,
        }}
        if tts.is_empty() {
            self.render.cx.span_fatal(sp, "expected expression for this splice");
        } else {
            self.new_rust_parser(tts).parse_expr()
        }
    }

    fn element(&mut self, sp: Span, name: &str) {
        if self.in_attr {
            self.render.cx.span_err(sp, "unexpected element, you silly bumpkin");
            return;
        }
        self.render.element_open_start(name);
        self.attrs();
        self.render.element_open_end();
        if let [slash!(), ..] = self.input {
            self.shift(1);
        } else {
            self.markup();
            self.render.element_close(name);
        }
    }

    fn attrs(&mut self) {
        loop { match self.input {
            [ident!(name), eq!(), ..] => {
                // Non-empty attribute
                self.shift(2);
                self.render.attribute_start(name.as_str());
                {
                    // Parse a value under an attribute context
                    let mut in_attr = true;
                    mem::swap(&mut self.in_attr, &mut in_attr);
                    self.markup();
                    mem::swap(&mut self.in_attr, &mut in_attr);
                }
                self.render.attribute_end();
            },
            [ident!(name), question!(), ..] => {
                // Empty attribute
                self.shift(2);
                if let [ref tt @ eq!(), ..] = self.input {
                    // Toggle the attribute based on a boolean expression
                    self.shift(1);
                    let cond = self.splice(tt.get_span());
                    // Silence "unnecessary parentheses" warnings
                    let cond = strip_outer_parens(cond).to_tokens(self.render.cx);
                    let body = {
                        let mut r = self.render.fork();
                        r.attribute_empty(name.as_str());
                        r.into_stmts()
                    };
                    self.render.emit_if(cond, body, None);
                } else {
                    // Write the attribute unconditionally
                    self.render.attribute_empty(name.as_str());
                }
            },
            _ => return,
        }}
    }

    fn block(&mut self, sp: Span, tts: &[TokenTree]) -> Vec<P<Stmt>> {
        let mut parse = Parser {
            in_attr: self.in_attr,
            input: tts,
            span: sp,
            render: self.render.fork(),
        };
        parse.markups();
        parse.into_render().into_stmts()
    }
}

/// Convert a literal to a string.
fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> {
    use syntax::ast::Lit_::*;
    let mut result = String::new();
    if minus {
        result.push('-');
    }
    match lit.node {
        LitStr(s, _) => result.push_str(&s),
        LitBinary(..) | LitByte(..) => {
            cx.span_err(lit.span, "cannot splice binary data");
            return None;
        },
        LitChar(c) => result.push(c),
        LitInt(x, _) => result.push_str(&x.to_string()),
        LitFloat(s, _) | LitFloatUnsuffixed(s) => result.push_str(&s),
        LitBool(b) => result.push_str(if b { "true" } else { "false" }),
    };
    Some(result)
}

/// If the expression is wrapped in parentheses, strip them off.
fn strip_outer_parens(expr: P<Expr>) -> P<Expr> {
    expr.and_then(|expr| match expr {
        Expr { node: ExprParen(inner), .. } => inner,
        expr => P(expr),
    })
}
<|endoftext|>