<commit_before>//! The main event loop which performs I/O on the pseudoterminal
use std::borrow::Cow;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Write};
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use mio::{self, Events, PollOpt, Ready};
use mio::unix::EventedFd;

use ansi;
use display;
use event;
use term::Term;
use util::thread;
use sync::FairMutex;

/// Messages that may be sent to the `EventLoop`
#[derive(Debug)]
pub enum Msg {
    /// Data that should be written to the pty
    Input(Cow<'static, [u8]>),
}

/// The main event!.. loop.
///
/// Handles all the pty I/O and runs the pty parser which updates terminal
/// state.
pub struct EventLoop<Io> {
    poll: mio::Poll,
    pty: Io,
    rx: mio::channel::Receiver<Msg>,
    tx: mio::channel::Sender<Msg>,
    terminal: Arc<FairMutex<Term>>,
    display: display::Notifier,
    ref_test: bool,
}

/// Helper type which tracks how much of a buffer has been written.
struct Writing {
    source: Cow<'static, [u8]>,
    written: usize,
}

/// All of the mutable state needed to run the event loop
///
/// Contains list of items to write, current write state, etc. Anything that
/// would otherwise be mutated on the `EventLoop` goes here.
pub struct State {
    write_list: VecDeque<Cow<'static, [u8]>>,
    writing: Option<Writing>,
    parser: ansi::Processor,
}

pub struct Notifier(pub ::mio::channel::Sender<Msg>);

impl event::Notify for Notifier {
    fn notify<B>(&mut self, bytes: B)
        where B: Into<Cow<'static, [u8]>>
    {
        let bytes = bytes.into();
        match self.0.send(Msg::Input(bytes)) {
            Ok(_) => (),
            Err(_) => panic!("expected send event loop msg"),
        }
    }
}

impl Default for State {
    fn default() -> State {
        State {
            write_list: VecDeque::new(),
            parser: ansi::Processor::new(),
            writing: None,
        }
    }
}

impl State {
    #[inline]
    fn ensure_next(&mut self) {
        if self.writing.is_none() {
            self.goto_next();
        }
    }

    #[inline]
    fn goto_next(&mut self) {
        self.writing = self.write_list
            .pop_front()
            .map(Writing::new);
    }

    #[inline]
    fn take_current(&mut self) -> Option<Writing> {
        self.writing.take()
    }

    #[inline]
    fn needs_write(&self) -> bool {
        self.writing.is_some() || !self.write_list.is_empty()
    }

    #[inline]
    fn set_current(&mut self, new: Option<Writing>) {
        self.writing = new;
    }
}

impl Writing {
    #[inline]
    fn new(c: Cow<'static, [u8]>) -> Writing {
        Writing { source: c, written: 0 }
    }

    #[inline]
    fn advance(&mut self, n: usize) {
        self.written += n;
    }

    #[inline]
    fn remaining_bytes(&self) -> &[u8] {
        &self.source[self.written..]
    }

    #[inline]
    fn finished(&self) -> bool {
        self.written >= self.source.len()
    }
}

/// `mio::Token` for the event loop channel
const CHANNEL: mio::Token = mio::Token(0);

/// `mio::Token` for the pty file descriptor
const PTY: mio::Token = mio::Token(1);

impl<Io> EventLoop<Io>
    where Io: io::Read + io::Write + Send + AsRawFd + 'static
{
    /// Create a new event loop
    pub fn new(
        terminal: Arc<FairMutex<Term>>,
        display: display::Notifier,
        pty: Io,
        ref_test: bool,
    ) -> EventLoop<Io> {
        let (tx, rx) = ::mio::channel::channel();
        EventLoop {
            poll: mio::Poll::new().expect("create mio Poll"),
            pty: pty,
            tx: tx,
            rx: rx,
            terminal: terminal,
            display: display,
            ref_test: ref_test,
        }
    }

    pub fn channel(&self) -> mio::channel::Sender<Msg> {
        self.tx.clone()
    }

    // Drain the channel
    //
    // Returns true if items were received
    fn drain_recv_channel(&self, state: &mut State) -> bool {
        let mut received_item = false;
        while let Ok(msg) = self.rx.try_recv() {
            received_item = true;
            match msg {
                Msg::Input(input) => {
                    state.write_list.push_back(input);
                }
            }
        }

        received_item
    }

    #[inline]
    fn channel_event(&mut self, state: &mut State) {
        self.drain_recv_channel(state);

        self.poll.reregister(
            &self.rx, CHANNEL,
            Ready::readable(),
            PollOpt::edge() | PollOpt::oneshot()
        ).expect("reregister channel");

        if state.needs_write() {
            self.poll.reregister(
                &EventedFd(&self.pty.as_raw_fd()),
                PTY,
                Ready::readable() | Ready::writable(),
                PollOpt::edge() | PollOpt::oneshot()
            ).expect("reregister fd after channel recv");
        }
    }

    #[inline]
    fn pty_read<W>(
        &mut self,
        state: &mut State,
        buf: &mut [u8],
        mut writer: Option<&mut W>
    ) -> io::Result<()>
        where W: Write
    {
        loop {
            match self.pty.read(&mut buf[..]) {
                Ok(0) => break,
                Ok(got) => {
                    writer = writer.map(|w| {
                        w.write_all(&buf[..got]).unwrap(); w
                    });

                    let mut terminal = self.terminal.lock();
                    for byte in &buf[..got] {
                        state.parser.advance(&mut *terminal, *byte, &mut self.pty);
                    }

                    // Only request a draw if one hasn't already been requested.
                    //
                    // This is a performance optimization even if only for X11
                    // which is very expensive to hammer on the even loop wakeup
                    if !terminal.dirty {
                        self.display.notify();
                        terminal.dirty = true;

                        // Break for writing
                        //
                        // Want to prevent case where reading always returns
                        // data and sequences like `C-c` cannot be sent.
                        //
                        // Doing this check in !terminal.dirty will prevent the
                        // condition from being checked overzealously.
                        if state.writing.is_some()
                            || !state.write_list.is_empty()
                            || self.drain_recv_channel(state)
                        {
                            break;
                        }
                    }
                },
                Err(err) => {
                    match err.kind() {
                        ErrorKind::Interrupted |
                        ErrorKind::WouldBlock => break,
                        _ => return Err(err),
                    }
                }
            }
        }

        Ok(())
    }

    #[inline]
    fn pty_write(&mut self, state: &mut State) -> io::Result<()> {
        state.ensure_next();

        'write_many: while let Some(mut current) = state.take_current() {
            'write_one: loop {
                match self.pty.write(current.remaining_bytes()) {
                    Ok(0) => {
                        state.set_current(Some(current));
                        break 'write_many;
                    },
                    Ok(n) => {
                        current.advance(n);
                        if current.finished() {
                            state.goto_next();
                            break 'write_one;
                        }
                    },
                    Err(err) => {
                        state.set_current(Some(current));
                        match err.kind() {
                            ErrorKind::Interrupted |
                            ErrorKind::WouldBlock => break 'write_many,
                            _ => return Err(err),
                        }
                    }
                }

            }
        }

        Ok(())
    }

    pub fn spawn(
        mut self,
        state: Option<State>
    ) -> thread::JoinHandle<(EventLoop<Io>, State)> {
        thread::spawn_named("pty reader", move || {
            let mut state = state.unwrap_or_else(Default::default);
            let mut buf = [0u8; 4096];

            let fd = self.pty.as_raw_fd();
            let fd = EventedFd(&fd);

            let poll_opts = PollOpt::edge() | PollOpt::oneshot();

            self.poll.register(&self.rx, CHANNEL, Ready::readable(), poll_opts).unwrap();
            self.poll.register(&fd, PTY, Ready::readable(), poll_opts).unwrap();

            let mut events = Events::with_capacity(1024);

            let mut pipe = if self.ref_test {
                let file = File::create("./alacritty.recording")
                    .expect("create alacritty recording");
                Some(file)
            } else {
                None
            };

            'event_loop: loop {
                if let Err(err) = self.poll.poll(&mut events, None) {
                    match err.kind() {
                        ErrorKind::Interrupted => continue,
                        _ => panic!("EventLoop polling error: {:?}", err)
                    }
                }

                for event in events.iter() {
                    match event.token() {
                        CHANNEL => self.channel_event(&mut state),
                        PTY => {
                            let kind = event.kind();

                            if kind.is_hup() {
                                break 'event_loop;
                            }

                            if kind.is_readable() {
                                if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
                                    error!("Event loop exitting due to error: {} [{}:{}]",
                                           err, file!(), line!());
                                    break 'event_loop;
                                }

                                if ::tty::process_should_exit() {
                                    break 'event_loop;
                                }
                            }

                            if kind.is_writable() {
                                if let Err(err) = self.pty_write(&mut state) {
                                    error!("Event loop exitting due to error: {} [{}:{}]",
                                           err, file!(), line!());
                                    break 'event_loop;
                                }
                            }

                            // Figure out pty interest
                            let mut interest = Ready::readable();
                            if state.needs_write() {
                                interest.insert(Ready::writable());
                            }

                            // Reregister pty
                            self.poll
                                .reregister(&fd, PTY, interest, poll_opts)
                                .expect("register fd after read/write");
                        },
                        _ => (),
                    }
                }
            }

            let _ = self.poll.deregister(&self.rx);
            let _ = self.poll.deregister(&fd);

            (self, state)
        })
    }
}
<commit_msg>Fix #573. Ensure we don't write 0 bytes to pty<commit_after>//! The main event loop which performs I/O on the pseudoterminal
use std::borrow::Cow;
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Write};
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use mio::{self, Events, PollOpt, Ready};
use mio::unix::EventedFd;

use ansi;
use display;
use event;
use term::Term;
use util::thread;
use sync::FairMutex;

/// Messages that may be sent to the `EventLoop`
#[derive(Debug)]
pub enum Msg {
    /// Data that should be written to the pty
    Input(Cow<'static, [u8]>),
}

/// The main event!.. loop.
///
/// Handles all the pty I/O and runs the pty parser which updates terminal
/// state.
pub struct EventLoop<Io> {
    poll: mio::Poll,
    pty: Io,
    rx: mio::channel::Receiver<Msg>,
    tx: mio::channel::Sender<Msg>,
    terminal: Arc<FairMutex<Term>>,
    display: display::Notifier,
    ref_test: bool,
}

/// Helper type which tracks how much of a buffer has been written.
struct Writing {
    source: Cow<'static, [u8]>,
    written: usize,
}

/// All of the mutable state needed to run the event loop
///
/// Contains list of items to write, current write state, etc. Anything that
/// would otherwise be mutated on the `EventLoop` goes here.
pub struct State {
    write_list: VecDeque<Cow<'static, [u8]>>,
    writing: Option<Writing>,
    parser: ansi::Processor,
}

pub struct Notifier(pub ::mio::channel::Sender<Msg>);

impl event::Notify for Notifier {
    fn notify<B>(&mut self, bytes: B)
        where B: Into<Cow<'static, [u8]>>
    {
        let bytes = bytes.into();
        // terminal hangs if we send 0 bytes through.
        if bytes.len() == 0 {
            return
        }
        match self.0.send(Msg::Input(bytes)) {
            Ok(_) => (),
            Err(_) => panic!("expected send event loop msg"),
        }
    }
}

impl Default for State {
    fn default() -> State {
        State {
            write_list: VecDeque::new(),
            parser: ansi::Processor::new(),
            writing: None,
        }
    }
}

impl State {
    #[inline]
    fn ensure_next(&mut self) {
        if self.writing.is_none() {
            self.goto_next();
        }
    }

    #[inline]
    fn goto_next(&mut self) {
        self.writing = self.write_list
            .pop_front()
            .map(Writing::new);
    }

    #[inline]
    fn take_current(&mut self) -> Option<Writing> {
        self.writing.take()
    }

    #[inline]
    fn needs_write(&self) -> bool {
        self.writing.is_some() || !self.write_list.is_empty()
    }

    #[inline]
    fn set_current(&mut self, new: Option<Writing>) {
        self.writing = new;
    }
}

impl Writing {
    #[inline]
    fn new(c: Cow<'static, [u8]>) -> Writing {
        Writing { source: c, written: 0 }
    }

    #[inline]
    fn advance(&mut self, n: usize) {
        self.written += n;
    }

    #[inline]
    fn remaining_bytes(&self) -> &[u8] {
        &self.source[self.written..]
    }

    #[inline]
    fn finished(&self) -> bool {
        self.written >= self.source.len()
    }
}

/// `mio::Token` for the event loop channel
const CHANNEL: mio::Token = mio::Token(0);

/// `mio::Token` for the pty file descriptor
const PTY: mio::Token = mio::Token(1);

impl<Io> EventLoop<Io>
    where Io: io::Read + io::Write + Send + AsRawFd + 'static
{
    /// Create a new event loop
    pub fn new(
        terminal: Arc<FairMutex<Term>>,
        display: display::Notifier,
        pty: Io,
        ref_test: bool,
    ) -> EventLoop<Io> {
        let (tx, rx) = ::mio::channel::channel();
        EventLoop {
            poll: mio::Poll::new().expect("create mio Poll"),
            pty: pty,
            tx: tx,
            rx: rx,
            terminal: terminal,
            display: display,
            ref_test: ref_test,
        }
    }

    pub fn channel(&self) -> mio::channel::Sender<Msg> {
        self.tx.clone()
    }

    // Drain the channel
    //
    // Returns true if items were received
    fn drain_recv_channel(&self, state: &mut State) -> bool {
        let mut received_item = false;
        while let Ok(msg) = self.rx.try_recv() {
            received_item = true;
            match msg {
                Msg::Input(input) => {
                    state.write_list.push_back(input);
                }
            }
        }

        received_item
    }

    #[inline]
    fn channel_event(&mut self, state: &mut State) {
        self.drain_recv_channel(state);

        self.poll.reregister(
            &self.rx, CHANNEL,
            Ready::readable(),
            PollOpt::edge() | PollOpt::oneshot()
        ).expect("reregister channel");

        if state.needs_write() {
            self.poll.reregister(
                &EventedFd(&self.pty.as_raw_fd()),
                PTY,
                Ready::readable() | Ready::writable(),
                PollOpt::edge() | PollOpt::oneshot()
            ).expect("reregister fd after channel recv");
        }
    }

    #[inline]
    fn pty_read<W>(
        &mut self,
        state: &mut State,
        buf: &mut [u8],
        mut writer: Option<&mut W>
    ) -> io::Result<()>
        where W: Write
    {
        loop {
            match self.pty.read(&mut buf[..]) {
                Ok(0) => break,
                Ok(got) => {
                    writer = writer.map(|w| {
                        w.write_all(&buf[..got]).unwrap(); w
                    });

                    let mut terminal = self.terminal.lock();
                    for byte in &buf[..got] {
                        state.parser.advance(&mut *terminal, *byte, &mut self.pty);
                    }

                    // Only request a draw if one hasn't already been requested.
                    //
                    // This is a performance optimization even if only for X11
                    // which is very expensive to hammer on the even loop wakeup
                    if !terminal.dirty {
                        self.display.notify();
                        terminal.dirty = true;

                        // Break for writing
                        //
                        // Want to prevent case where reading always returns
                        // data and sequences like `C-c` cannot be sent.
                        //
                        // Doing this check in !terminal.dirty will prevent the
                        // condition from being checked overzealously.
                        if state.writing.is_some()
                            || !state.write_list.is_empty()
                            || self.drain_recv_channel(state)
                        {
                            break;
                        }
                    }
                },
                Err(err) => {
                    match err.kind() {
                        ErrorKind::Interrupted |
                        ErrorKind::WouldBlock => break,
                        _ => return Err(err),
                    }
                }
            }
        }

        Ok(())
    }

    #[inline]
    fn pty_write(&mut self, state: &mut State) -> io::Result<()> {
        state.ensure_next();

        'write_many: while let Some(mut current) = state.take_current() {
            'write_one: loop {
                match self.pty.write(current.remaining_bytes()) {
                    Ok(0) => {
                        state.set_current(Some(current));
                        break 'write_many;
                    },
                    Ok(n) => {
                        current.advance(n);
                        if current.finished() {
                            state.goto_next();
                            break 'write_one;
                        }
                    },
                    Err(err) => {
                        state.set_current(Some(current));
                        match err.kind() {
                            ErrorKind::Interrupted |
                            ErrorKind::WouldBlock => break 'write_many,
                            _ => return Err(err),
                        }
                    }
                }

            }
        }

        Ok(())
    }

    pub fn spawn(
        mut self,
        state: Option<State>
    ) -> thread::JoinHandle<(EventLoop<Io>, State)> {
        thread::spawn_named("pty reader", move || {
            let mut state = state.unwrap_or_else(Default::default);
            let mut buf = [0u8; 4096];

            let fd = self.pty.as_raw_fd();
            let fd = EventedFd(&fd);

            let poll_opts = PollOpt::edge() | PollOpt::oneshot();

            self.poll.register(&self.rx, CHANNEL, Ready::readable(), poll_opts).unwrap();
            self.poll.register(&fd, PTY, Ready::readable(), poll_opts).unwrap();

            let mut events = Events::with_capacity(1024);

            let mut pipe = if self.ref_test {
                let file = File::create("./alacritty.recording")
                    .expect("create alacritty recording");
                Some(file)
            } else {
                None
            };

            'event_loop: loop {
                if let Err(err) = self.poll.poll(&mut events, None) {
                    match err.kind() {
                        ErrorKind::Interrupted => continue,
                        _ => panic!("EventLoop polling error: {:?}", err)
                    }
                }

                for event in events.iter() {
                    match event.token() {
                        CHANNEL => self.channel_event(&mut state),
                        PTY => {
                            let kind = event.kind();

                            if kind.is_hup() {
                                break 'event_loop;
                            }

                            if kind.is_readable() {
                                if let Err(err) = self.pty_read(&mut state, &mut buf, pipe.as_mut()) {
                                    error!("Event loop exitting due to error: {} [{}:{}]",
                                           err, file!(), line!());
                                    break 'event_loop;
                                }

                                if ::tty::process_should_exit() {
                                    break 'event_loop;
                                }
                            }

                            if kind.is_writable() {
                                if let Err(err) = self.pty_write(&mut state) {
                                    error!("Event loop exitting due to error: {} [{}:{}]",
                                           err, file!(), line!());
                                    break 'event_loop;
                                }
                            }

                            // Figure out pty interest
                            let mut interest = Ready::readable();
                            if state.needs_write() {
                                interest.insert(Ready::writable());
                            }

                            // Reregister pty
                            self.poll
                                .reregister(&fd, PTY, interest, poll_opts)
                                .expect("register fd after read/write");
                        },
                        _ => (),
                    }
                }
            }

            let _ = self.poll.deregister(&self.rx);
            let _ = self.poll.deregister(&fd);

            (self, state)
        })
    }
}
<|endoftext|>


<commit_before>obj buf[T](tup(T,T,T) data) {
  fn get(int i) -> T {
    if (i == 0) {
      ret data._0;
    } else {
      if (i == 1) {
        ret data._1;
      } else {
        ret data._2;
      }
    }
  }

  fn take(&T t) {}
}

fn main() {
  let buf[int] b = buf[int](tup(1,2,3));
  log "constructed object";
  log b.get(0);
  log b.get(1);
  log b.get(2);
  check (b.get(0) == 1);
  check (b.get(1) == 2);
  check (b.get(2) == 3);
}
<commit_msg>Adjust testcase to cover issue #91, which was actually fixed back in commit f02f9cbf29c2fdc28baeaa5a25671d9be29d6eab.  Closes #91.<commit_after>obj buf[T](tup(T,T,T) data) {
  fn get(int i) -> T {
    if (i == 0) {
      ret data._0;
    } else {
      if (i == 1) {
        ret data._1;
      } else {
        ret data._2;
      }
    }
  }

  fn take(&T t) {}
  fn take2(T t) {}
}

fn main() {
  let buf[int] b = buf[int](tup(1,2,3));
  log "constructed object";
  log b.get(0);
  log b.get(1);
  log b.get(2);
  check (b.get(0) == 1);
  check (b.get(1) == 2);
  check (b.get(2) == 3);
  b.take2(0);
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    if let Some(path) = env::args().nth(1) {
        let mut contents = String::new();
        match File::open(path) {
            Ok(mut file) => {
                match file.read_to_string(&mut contents) {
                    Ok(_) => execute(contents),
                    Err(_) => println!("Found the file but failed to read it."),
                }
            }
            Err(_) => println!("Failed to open the file for reading."),
        };
    } else {
        println!("Please provide a path to some brainfuck source as the first argument.")
    }
}

fn execute(source: String) {
    println!("Executing: {}", source)
}

#[cfg(test)]
mod tests {
    #[test]
    fn basic() {
        assert!(1 == 1);
    }
}
<commit_msg>Most of the typing donw with lots of tests<commit_after>use std::env;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    if let Some(path) = env::args().nth(1) {
        let mut contents = String::new();
        match File::open(path) {
            Ok(mut file) => {
                match file.read_to_string(&mut contents) {
                    Ok(_) => println!("Would read and eval here."),
                    Err(_) => println!("Found the file but failed to read it."),
                }
            }
            Err(_) => println!("Failed to open the file for reading."),
        };
    } else {
        println!("Please provide a path to some brainfuck source as the first argument.")
    }
}

mod brainfuck {
    pub type Pointer = u32;

    #[derive(PartialEq)]
    #[derive(Debug)]
    pub enum Command {
        IncrementPointer,
        DecrementPointer,
        IncrementValue,
        DecrementValue,
        OutputValue,
        InputValue,
        ForwardsTo(Pointer),
        BackwardsTo(Pointer)
    }

    #[derive(PartialEq)]
    #[derive(Debug)]
    pub enum ReadResult {
        Program(Vec<Command>),
        UnmatchedJump
    }

    pub fn read(source: &str) -> ReadResult {
        let mut program: Vec<Command> = vec![];

        // find braces first

        return ReadResult::Program(program)
    }

    // pub fn eval(program: Vec<Command>) {
    // }
}

#[cfg(test)]
mod tests {
    use brainfuck::*;
    use brainfuck::Command::*;
    use brainfuck::ReadResult::*;

    #[test]
    fn read_empty() {
        let source = "";
        let expected = Program(vec![]);
        let actual = read(source);
        assert_eq!(expected, actual);
    }

    #[test]
    fn read_simple() {
        let source = "+>-<.,<.-+>,";
        let expected = Program(vec![
            IncrementValue,
            IncrementPointer,
            DecrementValue,
            DecrementPointer,
            OutputValue,
            InputValue,
            DecrementPointer,
            OutputValue,
            DecrementValue,
            IncrementValue,
            IncrementPointer,
            InputValue
        ]);
        let actual = read(source);
        assert_eq!(expected, actual);
    }

    #[test]
    fn read_garbage() {
        let source = ",.+lol,hey.>there<";
        let expected = Program(vec![
            InputValue,
            OutputValue,
            IncrementValue,
            InputValue,
            OutputValue,
            IncrementPointer,
            DecrementPointer
        ]);
        let actual = read(source);
        assert_eq!(expected, actual);
    }

    #[test]
    fn read_loop() {
        let source = "[->+<]";
        let expected = Program(vec![
            ForwardsTo(5),
            DecrementValue,
            IncrementPointer,
            IncrementValue,
            DecrementPointer,
            BackwardsTo(0)
        ]);
        let actual = read(source);
        assert_eq!(expected, actual);
    }

    #[test]
    fn read_bad_loop() {
        let source = "[,.[,.>+-][./.[]]";
        let expected = UnmatchedJump;
        let actual = read(source);
        assert_eq!(expected, actual);
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use constant::CONFIG_FILE_NAME;
use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::PathBuf;
use std::str::FromStr;

pub enum ConfigKey {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl ConfigKey {
    pub fn from<S: AsRef<str>>(key: S) -> Result<ConfigKey, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(ConfigKey::BurnAfter),
            "sweep.period" => Ok(ConfigKey::SweepPeriod),
            "sweep.time"   => Ok(ConfigKey::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            ConfigKey::BurnAfter   => "burn.after",
            ConfigKey::SweepPeriod => "sweep.period",
            ConfigKey::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

type EditableTomlCore = BTreeMap<String, BTreeMap<String, String>>;

struct EditableToml(EditableTomlCore);

impl EditableToml {
    fn from(toml: Toml) -> Result<Self, ConfigError> {
        toml::decode::<EditableTomlCore>(toml)
            .map(|c| EditableToml(c))
            .ok_or(ConfigError::new(ConfigErrorKind::Something))
    }

    fn overwrite<CK: Borrow<ConfigKey>>(&mut self, key: CK, value: String) {
        let &mut EditableToml(ref mut toml) = self;

        let (first, second) = key.borrow().to_pair();
        let second          = second.to_string();

        if toml.contains_key(first) {
            toml.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            toml.insert(first.to_string(), entry);
        }
    }

    fn to_toml(self) -> Toml {
        let EditableToml(core) = self;
        toml::encode(&core)
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), CONFIG_FILE_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_file()
    }

    fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    pub fn create(&self) -> Result<(), IoError> {
        super::create_setting_file(Self::path(), self.to_string())
    }

    fn _new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut editable = EditableToml(BTreeMap::new());
        editable.overwrite(ConfigKey::BurnAfter  , "2 weeks".to_string());
        editable.overwrite(ConfigKey::SweepPeriod, "daily"  .to_string());
        editable.overwrite(ConfigKey::SweepTime  , "00:00"  .to_string());

        Self::_new(editable.to_toml())
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(Self::path()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().next());
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::_new(toml))
    }

    fn get<CK: Borrow<ConfigKey>>(key: CK) -> Result<String, CliError> {
        let key    = key.borrow();
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                ConfigKey::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                ConfigKey::SweepPeriod => unimplemented!(),
                ConfigKey::SweepTime   => unimplemented!(),
            }));
        let value = try!(result);

        value
            .as_str()
            .map(|v| v.to_string())
            .ok_or(From::from(ConfigError::new(ConfigErrorKind::NonStringValue)))
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = ConfigKey::BurnAfter;

        let after       = try!(Self::get(&key));
        let after       = try!(Self::validate(key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }

    pub fn set<CK: Borrow<ConfigKey>>(mut self, key: CK, value: String) -> Result<Self, ConfigError> {
        let mut editable = try!(EditableToml::from(self.toml));

        editable.overwrite(key.borrow(), value);

        self.toml = editable.to_toml();

        Ok(self)
    }

    pub fn validate<CK: Borrow<ConfigKey>, S: AsRef<str>>(key: CK, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key.borrow() {
            ConfigKey::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            ConfigKey::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            ConfigKey::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }
}
<commit_msg>[Refactoring] Rename "toml" => "core"<commit_after>extern crate chrono;
extern crate regex;
extern crate toml;

use self::chrono::{Duration, NaiveTime};
use self::regex::Regex;
use self::toml::Value as Toml;

use constant::CONFIG_FILE_NAME;
use error::{CannotHappenError, CliError, ConfigError, ConfigErrorKind};
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::path::PathBuf;
use std::str::FromStr;

pub enum ConfigKey {
    BurnAfter,
    SweepPeriod,
    SweepTime,
}

impl ConfigKey {
    pub fn from<S: AsRef<str>>(key: S) -> Result<ConfigKey, ConfigError> {
        match key.as_ref().trim() {
            "burn.after"   => Ok(ConfigKey::BurnAfter),
            "sweep.period" => Ok(ConfigKey::SweepPeriod),
            "sweep.time"   => Ok(ConfigKey::SweepTime),
            _              => Err(ConfigError::new(ConfigErrorKind::InvalidKey)),
        }
    }

    fn to_str(&self) -> &str {
        match *self {
            ConfigKey::BurnAfter   => "burn.after",
            ConfigKey::SweepPeriod => "sweep.period",
            ConfigKey::SweepTime   => "sweep.time",
        }
    }

    fn to_pair(&self) -> (&str, &str) {
        let key = self
            .to_str()
            .split('.')
            .collect::<Vec<&str>>();
        (key[0], key[1]) // unsafe!
    }
}

type EditableTomlCore = BTreeMap<String, BTreeMap<String, String>>;

struct EditableToml(EditableTomlCore);

impl EditableToml {
    fn from(toml: Toml) -> Result<Self, ConfigError> {
        toml::decode::<EditableTomlCore>(toml)
            .map(|c| EditableToml(c))
            .ok_or(ConfigError::new(ConfigErrorKind::Something))
    }

    fn overwrite<CK: Borrow<ConfigKey>>(&mut self, key: CK, value: String) {
        let &mut EditableToml(ref mut core) = self;

        let (first, second) = key.borrow().to_pair();
        let second          = second.to_string();

        if core.contains_key(first) {
            core.get_mut(first).map(|e| e.insert(second, value));
        } else {
            let mut entry = BTreeMap::new();
            entry.insert(second, value);

            core.insert(first.to_string(), entry);
        }
    }

    fn to_toml(self) -> Toml {
        let EditableToml(core) = self;
        toml::encode(&core)
    }
}

pub struct Config {
    toml: Toml,
}

impl Config {
    fn path() -> PathBuf {
        path_buf![super::working_dir(), CONFIG_FILE_NAME]
    }

    pub fn exist() -> bool {
        Self::path().is_file()
    }

    fn to_string(&self) -> String {
        toml::encode_str(&self.toml)
    }

    pub fn create(&self) -> Result<(), IoError> {
        super::create_setting_file(Self::path(), self.to_string())
    }

    fn _new(toml: Toml) -> Self {
        Config { toml: toml }
    }

    pub fn default() -> Self {
        let mut editable = EditableToml(BTreeMap::new());
        editable.overwrite(ConfigKey::BurnAfter  , "2 weeks".to_string());
        editable.overwrite(ConfigKey::SweepPeriod, "daily"  .to_string());
        editable.overwrite(ConfigKey::SweepTime  , "00:00"  .to_string());

        Self::_new(editable.to_toml())
    }

    pub fn read() -> Result<Self, CliError> {
        let mut f = try!(File::open(Self::path()));

        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));

        let result = contents
            .parse()
            .map_err(|e: Vec<_>| e.into_iter().next());
        let toml = match result {
            Ok(toml)    => toml,
            Err(option) => match option {
                Some(e) => return Err(From::from(e)),
                None    => return Err(From::from(CannotHappenError)),
            },
        };

        Ok(Self::_new(toml))
    }

    fn get<CK: Borrow<ConfigKey>>(key: CK) -> Result<String, CliError> {
        let key    = key.borrow();
        let config = try!(Self::read()).toml;

        let result = config
            .lookup(key.to_str())
            .ok_or(ConfigError::new(match *key {
                ConfigKey::BurnAfter   => ConfigErrorKind::NotFoundBurnAfter,
                ConfigKey::SweepPeriod => unimplemented!(),
                ConfigKey::SweepTime   => unimplemented!(),
            }));
        let value = try!(result);

        value
            .as_str()
            .map(|v| v.to_string())
            .ok_or(From::from(ConfigError::new(ConfigErrorKind::NonStringValue)))
    }

    pub fn extract_burn_after() -> Result<Duration, CliError> {
        let key = ConfigKey::BurnAfter;

        let after       = try!(Self::get(&key));
        let after       = try!(Self::validate(key, after));
        let after       = after.split(' ').collect::<Vec<&str>>();
        let (num, unit) = (after[0], after[1]);                    // unsafe!
        let num         = try!(num.parse::<u32>()) as i64;

        let duration = match unit {
            "day"  | "days"  => Duration::days(num),
            "week" | "weeks" => Duration::weeks(num),
            _                => return Err(From::from(CannotHappenError)),
        };

        Ok(duration)
    }

    pub fn set<CK: Borrow<ConfigKey>>(mut self, key: CK, value: String) -> Result<Self, ConfigError> {
        let mut editable = try!(EditableToml::from(self.toml));

        editable.overwrite(key.borrow(), value);

        self.toml = editable.to_toml();

        Ok(self)
    }

    pub fn validate<CK: Borrow<ConfigKey>, S: AsRef<str>>(key: CK, value: S) -> Result<String, CliError> {
        let value = value.as_ref().trim();

        match *key.borrow() {
            ConfigKey::BurnAfter => {
                let pair = try!(Regex::new(r"^(?P<num>\d+)\s?(?P<unit>days?|weeks?)$"))
                    .captures(value)
                    .map_or((None, None), |caps| (caps.name("num"), caps.name("unit")));
                let (num, unit) = match pair {
                    (Some(num), Some(unit)) => (num, unit),
                    _                       => return Err(From::from(ConfigError::new(ConfigErrorKind::BurnAfter))),
                };
                Ok(format!("{} {}", num, unit))
            },
            ConfigKey::SweepPeriod => {
                match value {
                    "daily" | "weekly" => Ok(value.to_string()),
                    _                  => Err(From::from(ConfigError::new(ConfigErrorKind::SweepPeriod))),
                }
            },
            ConfigKey::SweepTime => {
                match NaiveTime::from_str(format!("{}:00", value).as_ref()) {
                    Ok(_)  => Ok(value.to_string()),
                    // should set Err(e) to Error::cause()
                    Err(_) => Err(From::from(ConfigError::new(ConfigErrorKind::SweepTime))),
                }
            },
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Takeru Ohta <phjgt308@gmail.com>
//
// This software is released under the MIT License,
// see the LICENSE file at the top-level directory.

use std::rc::Rc;
use std::collections::HashMap;
use binary_tree::NodeAddr;
use binary_tree::Trie as BinTreeTrie;
use binary_tree::Node as BinTreeNode;
use double_array::Base;
use double_array::Chck;
use double_array::Trie;
use double_array::allocator::Allocator;

pub struct Builder {
    memo: Memo,
    allocator: Allocator,
    nodes: Vec<u64>,
    exts: Vec<u32>,
}

type Memo = HashMap<NodeAddr, Base>;
type U24 = u32;

struct Node {
    base: Base,
    chck: Chck,
    is_terminal: bool,
    index: u32,
    info: NodeInfo,
}

enum NodeInfo {
    Type0 {
        id_offset: u8,
        child1: Option<u8>,
        child2: Option<u8>,
    },
    Type1 {
        id_offset: u16,
        child: Option<u8>,
    },
    Type2 {
        id_offset: U24,
    },
    Type3 {
        id_offset: u32,
    },
}

impl Node {
    pub fn new(parent_base: Base, bt_node: &BinTreeNode) -> Self {
        Node {
            base: 0,
            chck: bt_node.label,
            is_terminal: bt_node.is_terminal,
            index: parent_base + bt_node.label as u32,
            info: match bt_node.id_offset() {
                n if n < 0x100 => {
                    NodeInfo::Type0 {
                        id_offset: n as u8,
                        child1: None,
                        child2: None,
                    }
                }
                n if n < 0x10000 => {
                    NodeInfo::Type1 {
                        id_offset: n as u16,
                        child: None,
                    }
                }
                n if n < 0x1000000 => NodeInfo::Type2 { id_offset: n as U24 },
                n => NodeInfo::Type3 { id_offset: n },
            },
        }
    }

    pub fn try_add_child(&mut self, label: u8) -> bool {
        match &mut self.info {
            &mut NodeInfo::Type0{ref mut child1, ..} if child1.is_none() => {
                *child1 = Some(label);
                true
            }
            &mut NodeInfo::Type0{ref mut child2, ..} if child2.is_none() => {
                *child2 = Some(label);
                true
            }
            &mut NodeInfo::Type1{ref mut child, ..} if child.is_none() => {
                *child = Some(label);
                true
            }
            _ => false,
        }
    }
}

impl NodeInfo {
    pub fn type_id(&self) -> u8 {
        match self {
            &NodeInfo::Type0{..} => 0,
            &NodeInfo::Type1{..} => 1,
            &NodeInfo::Type2{..} => 2,
            &NodeInfo::Type3{..} => 3,
        }
    }
}

impl Builder {
    pub fn new() -> Self {
        Builder {
            memo: Memo::new(),
            allocator: Allocator::new(),
            nodes: Vec::new(),
            exts: Vec::new(),
        }
    }

    pub fn build(mut self, trie: BinTreeTrie) -> Trie {
        let bt_root = trie.to_node();
        let da_root = Node::new(0, &bt_root);
        self.build_impl(Rc::new(bt_root), da_root);
        Trie::new(self.nodes, self.exts)
    }

    fn build_impl(&mut self, mut bt_node: Rc<BinTreeNode>, mut da_node: Node) {
        let mut children;
        let mut memo_key;
        let mut do_memoize;
        loop {
            if bt_node.child.is_none() {
                // empty children
                self.fix_node(da_node, None);
                return;
            }

            memo_key = bt_node.child.as_ref().unwrap().addr();
            if let Some(base) = self.memo.get(&memo_key).cloned() {
                // have been memoized
                self.fix_node(da_node, Some(base));
                return;
            }

            match Rc::try_unwrap(bt_node) {
                Ok(mut bt_node) => {
                    do_memoize = false; // not shared
                    children = bt_node.take_children();
                }
                Err(bt_node) => {
                    do_memoize = true; // shared
                    children = bt_node.children();
                }
            };

            let mut children = children.clone();
            let only_child = children.next().unwrap();
            if children.next().is_some() {
                break;
            }
            if only_child.is_terminal {
                break;
            }
            if !da_node.try_add_child(only_child.label) {
                break;
            }
            bt_node = only_child.clone();
        }

        let base = {
            let mut buf = [0; 0x100];
            let len = (0..).zip(children.clone()).map(|(i, c)| buf[i] = c.label).count();

            let mut labels = &mut buf[0..len];
            labels.reverse();
            self.allocator.allocate(labels)
        };
        if do_memoize {
            self.memo.insert(memo_key, base);
        }
        self.fix_node(da_node, Some(base));
        for bt_child in children {
            let da_child = Node::new(base, &bt_child);
            self.build_impl(bt_child, da_child);
        }
    }

    fn fix_node(&mut self, mut node: Node, base: Option<Base>) {
        node.base = base.unwrap_or(node.base);
        let n = mask(node.base as u64, 0, 29) + mask(node.info.type_id() as u64, 29, 2) +
                mask(node.is_terminal as u64, 31, 1) +
                mask(node.chck as u64, 32, 8);
        let n = match &node.info {
            &NodeInfo::Type0{id_offset, child1, child2} => {
                n + mask(child1.unwrap_or(0) as u64, 40, 8) +
                mask(child2.unwrap_or(0) as u64, 48, 8) +
                mask(id_offset as u64, 56, 8)
            }
            &NodeInfo::Type1{id_offset, child} => {
                n + mask(child.unwrap_or(0) as u64, 40, 8) + mask(id_offset as u64, 48, 16)
            }
            &NodeInfo::Type2{id_offset} => n + mask(id_offset as u64, 40, 24),
            &NodeInfo::Type3{id_offset} => {
                self.exts.push(id_offset);
                n + mask((self.exts.len() - 1) as u64, 40, 24)
            }
        };
        if self.nodes.len() <= node.index as usize {
            self.nodes.resize(node.index as usize + 1, 0);
        }
        self.nodes[node.index as usize] = n;
    }
}

fn mask(x: u64, offset: usize, size: usize) -> u64 {
    (x & ((1 << size) - 1)) << offset
}
<commit_msg>Build a tree (double array) by ascending order for saving memory on typical case<commit_after>// Copyright (c) 2015 Takeru Ohta <phjgt308@gmail.com>
//
// This software is released under the MIT License,
// see the LICENSE file at the top-level directory.

use std::rc::Rc;
use std::collections::HashMap;
use binary_tree::NodeAddr;
use binary_tree::Trie as BinTreeTrie;
use binary_tree::Node as BinTreeNode;
use double_array::Base;
use double_array::Chck;
use double_array::Trie;
use double_array::allocator::Allocator;

pub struct Builder {
    memo: Memo,
    allocator: Allocator,
    nodes: Vec<u64>,
    exts: Vec<u32>,
}

type Memo = HashMap<NodeAddr, Base>;
type U24 = u32;

struct Node {
    chck: Chck,
    is_terminal: bool,
    index: u32,
    info: NodeInfo,
}

enum NodeInfo {
    Type0 {
        id_offset: u8,
        child1: Option<u8>,
        child2: Option<u8>,
    },
    Type1 {
        id_offset: u16,
        child: Option<u8>,
    },
    Type2 {
        id_offset: U24,
    },
    Type3 {
        id_offset: u32,
    },
}

impl Node {
    pub fn new(parent_base: Base, bt_node: &BinTreeNode) -> Self {
        Node {
            chck: bt_node.label,
            is_terminal: bt_node.is_terminal,
            index: parent_base + bt_node.label as u32,
            info: match bt_node.id_offset() {
                n if n < 0x100 => {
                    NodeInfo::Type0 {
                        id_offset: n as u8,
                        child1: None,
                        child2: None,
                    }
                }
                n if n < 0x10000 => {
                    NodeInfo::Type1 {
                        id_offset: n as u16,
                        child: None,
                    }
                }
                n if n < 0x1000000 => NodeInfo::Type2 { id_offset: n as U24 },
                n => NodeInfo::Type3 { id_offset: n },
            },
        }
    }

    pub fn try_add_child(&mut self, label: u8) -> bool {
        match &mut self.info {
            &mut NodeInfo::Type0{ref mut child1, ..} if child1.is_none() => {
                *child1 = Some(label);
                true
            }
            &mut NodeInfo::Type0{ref mut child2, ..} if child2.is_none() => {
                *child2 = Some(label);
                true
            }
            &mut NodeInfo::Type1{ref mut child, ..} if child.is_none() => {
                *child = Some(label);
                true
            }
            _ => false,
        }
    }
}

impl NodeInfo {
    pub fn type_id(&self) -> u8 {
        match self {
            &NodeInfo::Type0{..} => 0,
            &NodeInfo::Type1{..} => 1,
            &NodeInfo::Type2{..} => 2,
            &NodeInfo::Type3{..} => 3,
        }
    }
}

impl Builder {
    pub fn new() -> Self {
        Builder {
            memo: Memo::new(),
            allocator: Allocator::new(),
            nodes: Vec::new(),
            exts: Vec::new(),
        }
    }

    pub fn build(mut self, trie: BinTreeTrie) -> Trie {
        let bt_root = trie.to_node();
        let da_root = Node::new(0, &bt_root);
        self.build_impl(Rc::new(bt_root), da_root);
        Trie::new(self.nodes, self.exts)
    }

    fn build_impl(&mut self, mut bt_node: Rc<BinTreeNode>, mut da_node: Node) {
        let mut children: Vec<_>;
        let mut memo_key;
        let mut do_memoize;
        loop {
            if bt_node.child.is_none() {
                // empty children
                self.fix_node(da_node, 0);
                return;
            }

            memo_key = bt_node.child.as_ref().unwrap().addr();
            if let Some(base) = self.memo.get(&memo_key).cloned() {
                // have been memoized
                self.fix_node(da_node, base);
                return;
            }

            children = match Rc::try_unwrap(bt_node) {
                Ok(mut bt_node) => {
                    do_memoize = false; // not shared
                    bt_node.take_children().collect()
                }
                Err(bt_node) => {
                    do_memoize = true; // shared
                    bt_node.children().collect()
                }
            };
            children.reverse();

            if children.len() != 1 {
                break;
            }
            if children[0].is_terminal {
                break;
            }
            if !da_node.try_add_child(children[0].label) {
                break;
            }
            bt_node = children[0].clone();
        }

        let base = {
            let labels = children.iter().map(|c| c.label).collect::<Vec<_>>();
            self.allocator.allocate(&labels)
        };
        if do_memoize {
            self.memo.insert(memo_key, base);
        }
        self.fix_node(da_node, base);
        for bt_child in children.into_iter() {
            let da_child = Node::new(base, &bt_child);
            self.build_impl(bt_child, da_child);
        }
    }

    fn fix_node(&mut self, node: Node, base: Base) {
        let n = mask(base as u64, 0, 29) + mask(node.info.type_id() as u64, 29, 2) +
                mask(node.is_terminal as u64, 31, 1) +
                mask(node.chck as u64, 32, 8);
        let n = match &node.info {
            &NodeInfo::Type0{id_offset, child1, child2} => {
                n + mask(child1.unwrap_or(0) as u64, 40, 8) +
                mask(child2.unwrap_or(0) as u64, 48, 8) +
                mask(id_offset as u64, 56, 8)
            }
            &NodeInfo::Type1{id_offset, child} => {
                n + mask(child.unwrap_or(0) as u64, 40, 8) + mask(id_offset as u64, 48, 16)
            }
            &NodeInfo::Type2{id_offset} => n + mask(id_offset as u64, 40, 24),
            &NodeInfo::Type3{id_offset} => {
                self.exts.push(id_offset);
                n + mask((self.exts.len() - 1) as u64, 40, 24)
            }
        };
        if self.nodes.len() <= node.index as usize {
            self.nodes.resize(node.index as usize + 1, 0);
        }
        self.nodes[node.index as usize] = n;
    }
}

fn mask(x: u64, offset: usize, size: usize) -> u64 {
    (x & ((1 << size) - 1)) << offset
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

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

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

impl Copy for EnumValueDescriptor {}

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

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

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

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

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

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

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

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

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

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

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

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    pub fn is<E: 'static>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

#[cfg(rustc_nightly)]
mod cast_impl {
    use super::*;

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<commit_msg>Fix EnumDescriptor::value_by_name lifetime signature<commit_after>use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::descriptor::FileDescriptorProto;
use crate::enums::ProtobufEnum;
use crate::enums::ProtobufEnumOrUnknown;
use crate::reflect::find_message_or_enum::find_message_or_enum;
use crate::reflect::find_message_or_enum::MessageOrEnum;
use crate::reflect::ProtobufValue;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone)]
pub struct EnumValueDescriptor {
    proto: &'static EnumValueDescriptorProto,
    protobuf_value: &'static dyn ProtobufValue,
    get_descriptor: &'static dyn GetEnumDescriptor,
}

impl PartialEq for EnumValueDescriptor {
    fn eq(&self, other: &EnumValueDescriptor) -> bool {
        self.enum_descriptor() == other.enum_descriptor() && self.value() == other.value()
    }
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

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

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

impl Copy for EnumValueDescriptor {}

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

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

    /// Convert to generic `ProtobufValue`
    pub fn protobuf_value(&self) -> &'static dyn ProtobufValue {
        self.protobuf_value
    }

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

    /// Convert this value descriptor into proper enum object.
    ///
    /// See [`EnumDescriptor::cast`](crate::reflect::EnumDescriptor::cast) for more details.
    pub fn cast<E: 'static>(&self) -> Option<E> {
        self.enum_descriptor().cast(self.value())
    }
}

trait GetEnumDescriptor: Send + Sync + 'static {
    fn descriptor(&self) -> &EnumDescriptor;
    unsafe fn copy_to(&self, value: i32, dest: *mut ());
}

struct GetDescriptorImpl<E: ProtobufEnum>(marker::PhantomData<E>);

impl<E: ProtobufEnum> GetEnumDescriptor for GetDescriptorImpl<E> {
    fn descriptor(&self) -> &EnumDescriptor {
        E::enum_descriptor_static()
    }

    unsafe fn copy_to(&self, value: i32, dest: *mut ()) {
        let e = E::from_i32(value).expect("unknown value");
        (&e as *const E).copy_to(dest as *mut E, 1);
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
pub struct EnumDescriptor {
    full_name: String,
    proto: &'static EnumDescriptorProto,
    values: Vec<EnumValueDescriptor>,
    /// Type id of `<E>`
    type_id: TypeId,
    /// Type id of `<ProtobufEnumOrUnknown<E>>`
    enum_or_unknown_type_id: TypeId,

    #[cfg(not(rustc_nightly))]
    get_descriptor: &'static dyn GetEnumDescriptor,

    index_by_name: HashMap<String, usize>,
    index_by_number: HashMap<i32, usize>,
}

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

/// Identity comparison: message descriptor are equal if their addresses are equal
impl PartialEq for EnumDescriptor {
    fn eq(&self, other: &EnumDescriptor) -> bool {
        self as *const EnumDescriptor == other as *const EnumDescriptor
    }
}

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

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        &self.full_name[..]
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: ProtobufEnum>() -> &'static EnumDescriptor {
        E::enum_descriptor_static()
    }

    fn compute_full_name(
        package: &str,
        path_to_package: &str,
        proto: &EnumDescriptorProto,
    ) -> String {
        let mut full_name = package.to_owned();
        if path_to_package.len() != 0 {
            if full_name.len() != 0 {
                full_name.push('.');
            }
            full_name.push_str(path_to_package);
        }
        if full_name.len() != 0 {
            full_name.push('.');
        }
        full_name.push_str(proto.get_name());
        full_name
    }

    /// Construct `EnumDescriptor` given enum name and `FileDescriptorProto`.
    ///
    /// This function is called from generated code, and should rarely be called directly.
    ///
    /// This function is not a part of public API.
    #[doc(hidden)]
    pub fn new<E>(name_in_file: &'static str, file: &'static FileDescriptorProto) -> EnumDescriptor
    where
        E: ProtobufEnum,
    {
        let (path_to_package, proto) = match find_message_or_enum(file, name_in_file) {
            (path_to_package, MessageOrEnum::Enum(e)) => (path_to_package, e),
            (_, MessageOrEnum::Message(_)) => panic!("not an enum"),
        };

        let mut index_by_name = HashMap::new();
        let mut index_by_number = HashMap::new();
        for (i, v) in proto.value.iter().enumerate() {
            index_by_number.insert(v.get_number(), i);
            index_by_name.insert(v.get_name().to_string(), i);
        }

        let proto_values = &proto.value;
        let code_values = E::values();
        assert_eq!(proto_values.len(), code_values.len());

        let get_descriptor = &GetDescriptorImpl(marker::PhantomData::<E>);

        let values = proto_values
            .iter()
            .zip(code_values)
            .map(|(p, c)| EnumValueDescriptor {
                proto: p,
                protobuf_value: c,
                get_descriptor,
            })
            .collect();

        EnumDescriptor {
            full_name: EnumDescriptor::compute_full_name(
                file.get_package(),
                &path_to_package,
                &proto,
            ),
            proto,
            values,
            type_id: TypeId::of::<E>(),
            enum_or_unknown_type_id: TypeId::of::<ProtobufEnumOrUnknown<E>>(),
            #[cfg(not(rustc_nightly))]
            get_descriptor,
            index_by_name,
            index_by_number,
        }
    }

    /// This enum values
    pub fn values(&self) -> &[EnumValueDescriptor] {
        &self.values
    }

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

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

    pub(crate) fn value_by_number_or_default(&self, number: i32) -> &EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => &self.values()[0],
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    pub fn is<E: 'static>(&self) -> bool {
        TypeId::of::<E>() == self.type_id
    }

    /// Create enum object from given value.
    ///
    /// Type parameter `E` can be either [`ProtobufEnum`](crate::ProtobufEnum)
    /// or [`ProtobufEnumOrUnknown`](crate::ProtobufEnumOrUnknown).
    ///
    /// # Panics
    ///
    /// This operation panics of `E` is `ProtobufEnum` and `value` is unknown.
    pub fn cast<E: 'static>(&self, value: i32) -> Option<E> {
        if TypeId::of::<E>() == self.type_id {
            Some(self.cast_to_protobuf_enum(value))
        } else if TypeId::of::<E>() == self.enum_or_unknown_type_id {
            Some(self.cast_to_protobuf_enum_or_unknown(value))
        } else {
            None
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnum>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        unsafe {
            let mut r = mem::uninitialized();
            self.get_descriptor
                .copy_to(value, &mut r as *mut E as *mut ());
            r
        }
    }

    #[cfg(rustc_nightly)]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        <E as cast_impl::CastValueToProtobufEnumOrUnknown>::cast(value).unwrap()
    }

    #[cfg(not(rustc_nightly))]
    fn cast_to_protobuf_enum_or_unknown<E: 'static>(&self, value: i32) -> E {
        use std::mem;
        use std::ptr;
        debug_assert_eq!(mem::size_of::<E>(), mem::size_of::<i32>());
        unsafe {
            // This works because `ProtobufEnumOrUnknown<E>` is `#[repr(transparent)]`
            let mut r = mem::uninitialized();
            ptr::copy(&value, &mut r as *mut E as *mut i32, 1);
            r
        }
    }
}

#[cfg(rustc_nightly)]
mod cast_impl {
    use super::*;

    pub(crate) trait CastValueToProtobufEnumOrUnknown: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnumOrUnknown for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnumOrUnknown for ProtobufEnumOrUnknown<E> {
        fn cast(value: i32) -> Option<ProtobufEnumOrUnknown<E>> {
            Some(ProtobufEnumOrUnknown::from_i32(value))
        }
    }

    pub(crate) trait CastValueToProtobufEnum: Sized {
        fn cast(value: i32) -> Option<Self>;
    }

    impl<T> CastValueToProtobufEnum for T {
        default fn cast(_value: i32) -> Option<T> {
            None
        }
    }

    impl<E: ProtobufEnum> CastValueToProtobufEnum for E {
        fn cast(value: i32) -> Option<E> {
            Some(E::from_i32(value).expect(&format!("unknown enum value: {}", value)))
        }
    }
}
<|endoftext|>


<commit_before>use crate::authenticator_delegate::{AuthenticatorDelegate, DefaultAuthenticatorDelegate, Retry};
use crate::refresh::RefreshFlow;
use crate::storage::{hash_scopes, DiskTokenStorage, MemoryStorage, TokenStorage};
use crate::types::{ApplicationSecret, GetToken, RefreshResult, RequestError, Token};

use futures::prelude::*;

use std::error::Error;
use std::io;
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;

/// Authenticator abstracts different `GetToken` implementations behind one type and handles
/// caching received tokens. It's important to use it (instead of the flows directly) because
/// otherwise the user needs to be asked for new authorization every time a token is generated.
///
/// `ServiceAccountAccess` does not need (and does not work) with `Authenticator`, given that it
/// does not require interaction and implements its own caching. Use it directly.
///
/// NOTE: It is recommended to use a client constructed like this in order to prevent functions
/// like `hyper::run()` from hanging: `let client = hyper::Client::builder().keep_alive(false);`.
/// Due to token requests being rare, this should not result in a too bad performance problem.
struct AuthenticatorImpl<
    T: GetToken,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: hyper::client::connect::Connect,
> {
    client: hyper::Client<C>,
    inner: Arc<T>,
    store: Arc<S>,
    delegate: AD,
}

/// A trait implemented for any hyper::Client as well as teh DefaultHyperClient.
pub trait HyperClientBuilder {
    type Connector: hyper::client::connect::Connect;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
}

/// The builder value used when the default hyper client should be used.
pub struct DefaultHyperClient;
impl HyperClientBuilder for DefaultHyperClient {
    type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector> {
        hyper::Client::builder()
            .keep_alive(false)
            .build::<_, hyper::Body>(hyper_rustls::HttpsConnector::new())
    }
}

impl<C> HyperClientBuilder for hyper::Client<C>
where
    C: hyper::client::connect::Connect,
{
    type Connector = C;

    fn build_hyper_client(self) -> hyper::Client<C> {
        self
    }
}

/// An internal trait implemented by flows to be used by an authenticator.
pub trait AuthFlow<C> {
    type TokenGetter: GetToken;

    fn build_token_getter(self, client: hyper::Client<C>) -> Self::TokenGetter;
}

/// An authenticator can be used with `InstalledFlow`'s or `DeviceFlow`'s and
/// will refresh tokens as they expire as well as optionally persist tokens to
/// disk.
pub struct Authenticator<
    T: AuthFlow<C::Connector>,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: HyperClientBuilder,
> {
    client: C,
    token_getter: T,
    store: io::Result<S>,
    delegate: AD,
}

impl<T> Authenticator<T, MemoryStorage, DefaultAuthenticatorDelegate, DefaultHyperClient>
where
    T: AuthFlow<<DefaultHyperClient as HyperClientBuilder>::Connector>,
{
    /// Create a new authenticator with the provided flow. By default a new
    /// hyper::Client will be created the default authenticator delegate will be
    /// used, and tokens will not be persisted to disk.
    /// Accepted flow types are DeviceFlow and InstalledFlow.
    ///
    /// Examples
    /// ```
    /// use std::path::Path;
    /// use yup_oauth2::{ApplicationSecret, Authenticator, DeviceFlow};
    /// let creds = ApplicationSecret::default();
    /// let auth = Authenticator::new(DeviceFlow::new(creds)).build().unwrap();
    /// ```
    pub fn new(
        flow: T,
    ) -> Authenticator<T, MemoryStorage, DefaultAuthenticatorDelegate, DefaultHyperClient> {
        Authenticator {
            client: DefaultHyperClient,
            token_getter: flow,
            store: Ok(MemoryStorage::new()),
            delegate: DefaultAuthenticatorDelegate,
        }
    }
}

impl<T, S, AD, C> Authenticator<T, S, AD, C>
where
    T: AuthFlow<C::Connector>,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: HyperClientBuilder,
{
    /// Use the provided hyper client.
    pub fn hyper_client<NewC>(
        self,
        hyper_client: hyper::Client<NewC>,
    ) -> Authenticator<T, S, AD, hyper::Client<NewC>>
    where
        NewC: hyper::client::connect::Connect,
        T: AuthFlow<NewC>,
    {
        Authenticator {
            client: hyper_client,
            token_getter: self.token_getter,
            store: self.store,
            delegate: self.delegate,
        }
    }

    /// Persist tokens to disk in the provided filename.
    pub fn persist_tokens_to_disk<P: AsRef<Path>>(
        self,
        path: P,
    ) -> Authenticator<T, DiskTokenStorage, AD, C> {
        let disk_storage = DiskTokenStorage::new(path.as_ref().to_str().unwrap());
        Authenticator {
            client: self.client,
            token_getter: self.token_getter,
            store: disk_storage,
            delegate: self.delegate,
        }
    }

    /// Use the provided authenticator delegate.
    pub fn delegate<NewAD: AuthenticatorDelegate>(
        self,
        delegate: NewAD,
    ) -> Authenticator<T, S, NewAD, C> {
        Authenticator {
            client: self.client,
            token_getter: self.token_getter,
            store: self.store,
            delegate: delegate,
        }
    }

    /// Create the authenticator.
    pub fn build(self) -> io::Result<impl GetToken>
    where
        T::TokenGetter: 'static + GetToken,
        S: 'static + Send,
        AD: 'static,
        C::Connector: 'static + Clone + Send,
    {
        let client = self.client.build_hyper_client();
        let store = Arc::new(self.store?);
        let inner = Arc::new(self.token_getter.build_token_getter(client.clone()));

        Ok(AuthenticatorImpl {
            client,
            inner,
            store,
            delegate: self.delegate,
        })
    }
}

impl<GT, S, AD, C> AuthenticatorImpl<GT, S, AD, C>
where
    GT: 'static + GetToken,
    S: 'static + TokenStorage,
    AD: 'static + AuthenticatorDelegate,
    C: 'static + hyper::client::connect::Connect + Clone + Send,
{
    async fn get_token<T>(&self, scopes: &[T]) -> Result<Token, RequestError>
    where
        T: AsRef<str> + Sync,
    {
        let scope_key = hash_scopes(scopes);
        let store = self.store.clone();
        let delegate = &self.delegate;
        let client = self.client.clone();
        let appsecret = self.inner.application_secret();
        let gettoken = self.inner.clone();
        loop {
            match store.get(
                scope_key,
                scopes,
            ) {
                Ok(Some(t)) => {
                    if !t.expired() {
                        return Ok(t);
                    }
                    // Implement refresh flow.
                    let refresh_token = t.refresh_token.clone();
                    let store = store.clone();
                    let rr = RefreshFlow::refresh_token(
                        client.clone(),
                        appsecret,
                        refresh_token.unwrap(),
                    )
                    .await?;
                    match rr {
                        RefreshResult::Error(ref e) => {
                            delegate.token_refresh_failed(
                                format!("{}", e.description().to_string()),
                                &Some("the request has likely timed out".to_string()),
                            );
                            return Err(RequestError::Refresh(rr));
                        }
                        RefreshResult::RefreshError(ref s, ref ss) => {
                            delegate.token_refresh_failed(
                                format!("{} {}", s, ss.clone().map(|s| format!("({})", s)).unwrap_or("".to_string())),
                                &Some("the refresh token is likely invalid and your authorization has been revoked".to_string()),
                                );
                            return Err(RequestError::Refresh(rr));
                        }
                        RefreshResult::Success(t) => {
                            let x = store.set(
                                scope_key,
                                scopes,
                                Some(t.clone()),
                            );
                            if let Err(e) = x {
                                match delegate.token_storage_failure(true, &e) {
                                    Retry::Skip => return Ok(t),
                                    Retry::Abort => return Err(RequestError::Cache(Box::new(e))),
                                    Retry::After(d) => tokio::timer::delay_for(d).await,
                                }
                            } else {
                                return Ok(t);
                            }
                        }
                    }
                }
                Ok(None) => {
                    let store = store.clone();
                    let t = gettoken.token(scopes).await?;
                    if let Err(e) = store.set(
                        scope_key,
                        scopes,
                        Some(t.clone()),
                    ) {
                        match delegate.token_storage_failure(true, &e) {
                            Retry::Skip => return Ok(t),
                            Retry::Abort => return Err(RequestError::Cache(Box::new(e))),
                            Retry::After(d) => tokio::timer::delay_for(d).await,
                        }
                    } else {
                        return Ok(t);
                    }
                }
                Err(err) => match delegate.token_storage_failure(false, &err) {
                    Retry::Abort | Retry::Skip => return Err(RequestError::Cache(Box::new(err))),
                    Retry::After(d) => tokio::timer::delay_for(d).await,
                },
            }
        }
    }
}

impl<
        GT: 'static + GetToken,
        S: 'static + TokenStorage,
        AD: 'static + AuthenticatorDelegate,
        C: 'static + hyper::client::connect::Connect + Clone + Send,
    > GetToken for AuthenticatorImpl<GT, S, AD, C>
{
    /// Returns the API Key of the inner flow.
    fn api_key(&self) -> Option<String> {
        self.inner.api_key()
    }
    /// Returns the application secret of the inner flow.
    fn application_secret(&self) -> &ApplicationSecret {
        self.inner.application_secret()
    }

    fn token<'a, T>(
        &'a self,
        scopes: &'a [T],
    ) -> Pin<Box<dyn Future<Output = Result<Token, RequestError>> + Send + 'a>>
    where
        T: AsRef<str> + Sync,
    {
        Box::pin(self.get_token(scopes))
    }
}
<commit_msg>Authenticator.store no longer needs to be reference counted.<commit_after>use crate::authenticator_delegate::{AuthenticatorDelegate, DefaultAuthenticatorDelegate, Retry};
use crate::refresh::RefreshFlow;
use crate::storage::{hash_scopes, DiskTokenStorage, MemoryStorage, TokenStorage};
use crate::types::{ApplicationSecret, GetToken, RefreshResult, RequestError, Token};

use futures::prelude::*;

use std::error::Error;
use std::io;
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;

/// Authenticator abstracts different `GetToken` implementations behind one type and handles
/// caching received tokens. It's important to use it (instead of the flows directly) because
/// otherwise the user needs to be asked for new authorization every time a token is generated.
///
/// `ServiceAccountAccess` does not need (and does not work) with `Authenticator`, given that it
/// does not require interaction and implements its own caching. Use it directly.
///
/// NOTE: It is recommended to use a client constructed like this in order to prevent functions
/// like `hyper::run()` from hanging: `let client = hyper::Client::builder().keep_alive(false);`.
/// Due to token requests being rare, this should not result in a too bad performance problem.
struct AuthenticatorImpl<
    T: GetToken,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: hyper::client::connect::Connect,
> {
    client: hyper::Client<C>,
    inner: Arc<T>,
    store: S,
    delegate: AD,
}

/// A trait implemented for any hyper::Client as well as teh DefaultHyperClient.
pub trait HyperClientBuilder {
    type Connector: hyper::client::connect::Connect;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
}

/// The builder value used when the default hyper client should be used.
pub struct DefaultHyperClient;
impl HyperClientBuilder for DefaultHyperClient {
    type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;

    fn build_hyper_client(self) -> hyper::Client<Self::Connector> {
        hyper::Client::builder()
            .keep_alive(false)
            .build::<_, hyper::Body>(hyper_rustls::HttpsConnector::new())
    }
}

impl<C> HyperClientBuilder for hyper::Client<C>
where
    C: hyper::client::connect::Connect,
{
    type Connector = C;

    fn build_hyper_client(self) -> hyper::Client<C> {
        self
    }
}

/// An internal trait implemented by flows to be used by an authenticator.
pub trait AuthFlow<C> {
    type TokenGetter: GetToken;

    fn build_token_getter(self, client: hyper::Client<C>) -> Self::TokenGetter;
}

/// An authenticator can be used with `InstalledFlow`'s or `DeviceFlow`'s and
/// will refresh tokens as they expire as well as optionally persist tokens to
/// disk.
pub struct Authenticator<
    T: AuthFlow<C::Connector>,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: HyperClientBuilder,
> {
    client: C,
    token_getter: T,
    store: io::Result<S>,
    delegate: AD,
}

impl<T> Authenticator<T, MemoryStorage, DefaultAuthenticatorDelegate, DefaultHyperClient>
where
    T: AuthFlow<<DefaultHyperClient as HyperClientBuilder>::Connector>,
{
    /// Create a new authenticator with the provided flow. By default a new
    /// hyper::Client will be created the default authenticator delegate will be
    /// used, and tokens will not be persisted to disk.
    /// Accepted flow types are DeviceFlow and InstalledFlow.
    ///
    /// Examples
    /// ```
    /// use std::path::Path;
    /// use yup_oauth2::{ApplicationSecret, Authenticator, DeviceFlow};
    /// let creds = ApplicationSecret::default();
    /// let auth = Authenticator::new(DeviceFlow::new(creds)).build().unwrap();
    /// ```
    pub fn new(
        flow: T,
    ) -> Authenticator<T, MemoryStorage, DefaultAuthenticatorDelegate, DefaultHyperClient> {
        Authenticator {
            client: DefaultHyperClient,
            token_getter: flow,
            store: Ok(MemoryStorage::new()),
            delegate: DefaultAuthenticatorDelegate,
        }
    }
}

impl<T, S, AD, C> Authenticator<T, S, AD, C>
where
    T: AuthFlow<C::Connector>,
    S: TokenStorage,
    AD: AuthenticatorDelegate,
    C: HyperClientBuilder,
{
    /// Use the provided hyper client.
    pub fn hyper_client<NewC>(
        self,
        hyper_client: hyper::Client<NewC>,
    ) -> Authenticator<T, S, AD, hyper::Client<NewC>>
    where
        NewC: hyper::client::connect::Connect,
        T: AuthFlow<NewC>,
    {
        Authenticator {
            client: hyper_client,
            token_getter: self.token_getter,
            store: self.store,
            delegate: self.delegate,
        }
    }

    /// Persist tokens to disk in the provided filename.
    pub fn persist_tokens_to_disk<P: AsRef<Path>>(
        self,
        path: P,
    ) -> Authenticator<T, DiskTokenStorage, AD, C> {
        let disk_storage = DiskTokenStorage::new(path.as_ref().to_str().unwrap());
        Authenticator {
            client: self.client,
            token_getter: self.token_getter,
            store: disk_storage,
            delegate: self.delegate,
        }
    }

    /// Use the provided authenticator delegate.
    pub fn delegate<NewAD: AuthenticatorDelegate>(
        self,
        delegate: NewAD,
    ) -> Authenticator<T, S, NewAD, C> {
        Authenticator {
            client: self.client,
            token_getter: self.token_getter,
            store: self.store,
            delegate: delegate,
        }
    }

    /// Create the authenticator.
    pub fn build(self) -> io::Result<impl GetToken>
    where
        T::TokenGetter: 'static + GetToken,
        S: 'static + Send,
        AD: 'static,
        C::Connector: 'static + Clone + Send,
    {
        let client = self.client.build_hyper_client();
        let store = self.store?;
        let inner = Arc::new(self.token_getter.build_token_getter(client.clone()));

        Ok(AuthenticatorImpl {
            client,
            inner,
            store,
            delegate: self.delegate,
        })
    }
}

impl<GT, S, AD, C> AuthenticatorImpl<GT, S, AD, C>
where
    GT: 'static + GetToken,
    S: 'static + TokenStorage,
    AD: 'static + AuthenticatorDelegate,
    C: 'static + hyper::client::connect::Connect + Clone + Send,
{
    async fn get_token<T>(&self, scopes: &[T]) -> Result<Token, RequestError>
    where
        T: AsRef<str> + Sync,
    {
        let scope_key = hash_scopes(scopes);
        let store = &self.store;
        let delegate = &self.delegate;
        let client = self.client.clone();
        let appsecret = self.inner.application_secret();
        let gettoken = self.inner.clone();
        loop {
            match store.get(
                scope_key,
                scopes,
            ) {
                Ok(Some(t)) => {
                    if !t.expired() {
                        return Ok(t);
                    }
                    // Implement refresh flow.
                    let refresh_token = t.refresh_token.clone();
                    let rr = RefreshFlow::refresh_token(
                        client.clone(),
                        appsecret,
                        refresh_token.unwrap(),
                    )
                    .await?;
                    match rr {
                        RefreshResult::Error(ref e) => {
                            delegate.token_refresh_failed(
                                format!("{}", e.description().to_string()),
                                &Some("the request has likely timed out".to_string()),
                            );
                            return Err(RequestError::Refresh(rr));
                        }
                        RefreshResult::RefreshError(ref s, ref ss) => {
                            delegate.token_refresh_failed(
                                format!("{} {}", s, ss.clone().map(|s| format!("({})", s)).unwrap_or("".to_string())),
                                &Some("the refresh token is likely invalid and your authorization has been revoked".to_string()),
                                );
                            return Err(RequestError::Refresh(rr));
                        }
                        RefreshResult::Success(t) => {
                            let x = store.set(
                                scope_key,
                                scopes,
                                Some(t.clone()),
                            );
                            if let Err(e) = x {
                                match delegate.token_storage_failure(true, &e) {
                                    Retry::Skip => return Ok(t),
                                    Retry::Abort => return Err(RequestError::Cache(Box::new(e))),
                                    Retry::After(d) => tokio::timer::delay_for(d).await,
                                }
                            } else {
                                return Ok(t);
                            }
                        }
                    }
                }
                Ok(None) => {
                    let t = gettoken.token(scopes).await?;
                    if let Err(e) = store.set(
                        scope_key,
                        scopes,
                        Some(t.clone()),
                    ) {
                        match delegate.token_storage_failure(true, &e) {
                            Retry::Skip => return Ok(t),
                            Retry::Abort => return Err(RequestError::Cache(Box::new(e))),
                            Retry::After(d) => tokio::timer::delay_for(d).await,
                        }
                    } else {
                        return Ok(t);
                    }
                }
                Err(err) => match delegate.token_storage_failure(false, &err) {
                    Retry::Abort | Retry::Skip => return Err(RequestError::Cache(Box::new(err))),
                    Retry::After(d) => tokio::timer::delay_for(d).await,
                },
            }
        }
    }
}

impl<
        GT: 'static + GetToken,
        S: 'static + TokenStorage,
        AD: 'static + AuthenticatorDelegate,
        C: 'static + hyper::client::connect::Connect + Clone + Send,
    > GetToken for AuthenticatorImpl<GT, S, AD, C>
{
    /// Returns the API Key of the inner flow.
    fn api_key(&self) -> Option<String> {
        self.inner.api_key()
    }
    /// Returns the application secret of the inner flow.
    fn application_secret(&self) -> &ApplicationSecret {
        self.inner.application_secret()
    }

    fn token<'a, T>(
        &'a self,
        scopes: &'a [T],
    ) -> Pin<Box<dyn Future<Output = Result<Token, RequestError>> + Send + 'a>>
    where
        T: AsRef<str> + Sync,
    {
        Box::pin(self.get_token(scopes))
    }
}
<|endoftext|>