<commit_before>use std::cmp;
use std::fmt;
use std::io;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use crate::{Doc, DocPtr};

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W> {
    upstream: W,
}

impl<W> IoWrite<W> {
    pub fn new(upstream: W) -> IoWrite<W> {
        IoWrite { upstream }
    }
}

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.upstream.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.upstream.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W> {
    upstream: W,
}

impl<W> FmtWrite<W> {
    pub fn new(upstream: W) -> FmtWrite<W> {
        FmtWrite { upstream }
    }
}

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.upstream.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    upstream: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(upstream: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            upstream,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.upstream.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.upstream.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.upstream.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.upstream.set_color(previous),
            None => self.upstream.reset(),
        }
    }
}

macro_rules! make_spaces {
            () => { "" };
            ($s: tt $($t: tt)*) => { concat!("          ", make_spaces!($($t)*)) };
        }

pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);

#[inline]
pub fn best<'a, W, T, A>(doc: &Doc<'a, T, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    T: DocPtr<'a, A> + 'a,
    W: ?Sized + RenderAnnotated<A>,
{
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    enum Mode {
        Break,
        Flat,
    }

    type Cmd<'d, 'a, T, A> = (usize, Mode, &'d Doc<'a, T, A>);

    fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        out.write_str_all("\n")?;
        write_spaces(ind, out)
    }

    fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        let mut inserted = 0;
        while inserted < spaces {
            let insert = cmp::min(SPACES.len(), spaces - inserted);
            inserted += out.write_str(&SPACES[..insert])?;
        }

        Ok(())
    }

    fn fitting<'a, 'd, T, A>(
        temp_arena: &'d typed_arena::Arena<T>,
        next: &'d Doc<'a, T, A>,
        bcmds: &[Cmd<'d, 'a, T, A>],
        fcmds: &mut Vec<&'d Doc<'a, T, A>>,
        mut pos: usize,
        width: usize,
        ind: usize,
        newline_fits: fn(Mode) -> bool,
    ) -> bool
    where
        T: DocPtr<'a, A>,
    {
        let mut bidx = bcmds.len();
        fcmds.clear(); // clear from previous calls from best
        fcmds.push(next);

        let mut mode = Mode::Flat;
        loop {
            let mut doc = match fcmds.pop() {
                None => {
                    if bidx == 0 {
                        // All commands have been processed
                        return true;
                    } else {
                        bidx -= 1;
                        mode = Mode::Break;
                        bcmds[bidx].2
                    }
                }
                Some(cmd) => cmd,
            };

            loop {
                match *doc {
                    Doc::Nil => {}
                    Doc::Append(ref ldoc, ref rdoc) => {
                        fcmds.push(rdoc);
                        // Since appended documents often appear in sequence on the left side we
                        // gain a slight performance increase by batching these pushes (avoiding
                        // to push and directly pop `Append` documents)
                        doc = ldoc;
                        while let Doc::Append(ref l, ref r) = *doc {
                            fcmds.push(r);
                            doc = l;
                        }
                        continue;
                    }
                    // Newlines inside the group makes it not fit, but those outside lets it
                    // fit on the current line
                    Doc::Line => return newline_fits(mode),
                    Doc::BorrowedText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::OwnedText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::SmallText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::FlatAlt(ref b, ref f) => {
                        doc = match mode {
                            Mode::Break => b,
                            Mode::Flat => f,
                        };
                        continue;
                    }

                    Doc::Column(ref f) => {
                        doc = temp_arena.alloc(f(pos));
                        continue;
                    }
                    Doc::Nesting(ref f) => {
                        doc = temp_arena.alloc(f(ind));
                        continue;
                    }
                    Doc::Nest(_, ref next)
                    | Doc::Group(ref next)
                    | Doc::Annotated(_, ref next)
                    | Doc::Union(_, ref next) => {
                        doc = next;
                        continue;
                    }
                }
                break;
            }
        }
    }

    let temp_arena = typed_arena::Arena::new();

    let mut pos = 0;
    let mut bcmds = vec![(0, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some(mut cmd) = bcmds.pop() {
        loop {
            let (ind, mode, doc) = cmd;
            match *doc {
                Doc::Nil => {}
                Doc::Append(ref ldoc, ref rdoc) => {
                    bcmds.push((ind, mode, rdoc));
                    let mut doc = ldoc;
                    while let Doc::Append(ref l, ref r) = **doc {
                        bcmds.push((ind, mode, r));
                        doc = l;
                    }
                    cmd = (ind, mode, doc);
                    continue;
                }
                Doc::FlatAlt(ref b, ref f) => {
                    cmd = (
                        ind,
                        mode,
                        match mode {
                            Mode::Break => b,
                            Mode::Flat => f,
                        },
                    );
                    continue;
                }
                Doc::Group(ref doc) => match mode {
                    Mode::Flat => {
                        cmd = (ind, Mode::Flat, doc);
                        continue;
                    }
                    Mode::Break => {
                        cmd = if fitting(
                            &temp_arena,
                            doc,
                            &bcmds,
                            &mut fcmds,
                            pos,
                            width,
                            ind,
                            |mode| mode == Mode::Break,
                        ) {
                            (ind, Mode::Flat, &**doc)
                        } else {
                            (ind, Mode::Break, doc)
                        };
                        continue;
                    }
                },
                Doc::Nest(off, ref doc) => {
                    cmd = ((ind as isize).saturating_add(off) as usize, mode, doc);
                    continue;
                }
                Doc::Line => {
                    write_newline(ind, out)?;
                    pos = ind;
                }
                Doc::OwnedText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::BorrowedText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::SmallText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::Annotated(ref ann, ref doc) => {
                    out.push_annotation(ann)?;
                    annotation_levels.push(bcmds.len());
                    cmd = (ind, mode, doc);
                    continue;
                }
                Doc::Union(ref l, ref r) => {
                    cmd = if fitting(&temp_arena, l, &bcmds, &mut fcmds, pos, width, ind, |_| {
                        true
                    }) {
                        (ind, mode, l)
                    } else {
                        (ind, mode, r)
                    };
                    continue;
                }
                Doc::Column(ref f) => {
                    cmd = (ind, mode, temp_arena.alloc(f(pos)));
                    continue;
                }
                Doc::Nesting(ref f) => {
                    cmd = (ind, mode, temp_arena.alloc(f(ind)));
                    continue;
                }
            }

            break;
        }
        while annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            out.pop_annotation()?;
        }
    }

    Ok(())
}
<commit_msg>perf: Handle doc appending on both left and right<commit_after>use std::cmp;
use std::fmt;
use std::io;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use crate::{Doc, DocPtr};

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W> {
    upstream: W,
}

impl<W> IoWrite<W> {
    pub fn new(upstream: W) -> IoWrite<W> {
        IoWrite { upstream }
    }
}

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.upstream.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.upstream.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W> {
    upstream: W,
}

impl<W> FmtWrite<W> {
    pub fn new(upstream: W) -> FmtWrite<W> {
        FmtWrite { upstream }
    }
}

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.upstream.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    upstream: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(upstream: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            upstream,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.upstream.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.upstream.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.upstream.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.upstream.set_color(previous),
            None => self.upstream.reset(),
        }
    }
}

macro_rules! make_spaces {
            () => { "" };
            ($s: tt $($t: tt)*) => { concat!("          ", make_spaces!($($t)*)) };
        }

pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);

fn append_docs2<'a, 'd, T, A>(
    ldoc: &'d Doc<'a, T, A>,
    rdoc: &'d Doc<'a, T, A>,
    mut consumer: impl FnMut(&'d Doc<'a, T, A>),
) -> &'d Doc<'a, T, A>
where
    T: DocPtr<'a, A>,
{
    let d = append_docs(rdoc, &mut consumer);
    consumer(d);
    append_docs(ldoc, &mut consumer)
}

fn append_docs<'a, 'd, T, A>(
    mut doc: &'d Doc<'a, T, A>,
    consumer: &mut impl FnMut(&'d Doc<'a, T, A>),
) -> &'d Doc<'a, T, A>
where
    T: DocPtr<'a, A>,
{
    loop {
        // Since appended documents often appear in sequence on the left side we
        // gain a slight performance increase by batching these pushes (avoiding
        // to push and directly pop `Append` documents)
        match doc {
            Doc::Append(l, r) => {
                let d = append_docs(r, consumer);
                consumer(d);
                doc = l;
            }
            _ => return doc,
        }
    }
}

#[inline]
pub fn best<'a, W, T, A>(doc: &Doc<'a, T, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    T: DocPtr<'a, A> + 'a,
    W: ?Sized + RenderAnnotated<A>,
{
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    enum Mode {
        Break,
        Flat,
    }

    type Cmd<'d, 'a, T, A> = (usize, Mode, &'d Doc<'a, T, A>);

    fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        out.write_str_all("\n")?;
        write_spaces(ind, out)
    }

    fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        let mut inserted = 0;
        while inserted < spaces {
            let insert = cmp::min(SPACES.len(), spaces - inserted);
            inserted += out.write_str(&SPACES[..insert])?;
        }

        Ok(())
    }

    fn fitting<'a, 'd, T, A>(
        temp_arena: &'d typed_arena::Arena<T>,
        next: &'d Doc<'a, T, A>,
        bcmds: &[Cmd<'d, 'a, T, A>],
        fcmds: &mut Vec<&'d Doc<'a, T, A>>,
        mut pos: usize,
        width: usize,
        ind: usize,
        newline_fits: fn(Mode) -> bool,
    ) -> bool
    where
        T: DocPtr<'a, A>,
    {
        let mut bidx = bcmds.len();
        fcmds.clear(); // clear from previous calls from best
        fcmds.push(next);

        let mut mode = Mode::Flat;
        loop {
            let mut doc = match fcmds.pop() {
                None => {
                    if bidx == 0 {
                        // All commands have been processed
                        return true;
                    } else {
                        bidx -= 1;
                        mode = Mode::Break;
                        bcmds[bidx].2
                    }
                }
                Some(cmd) => cmd,
            };

            loop {
                match *doc {
                    Doc::Nil => {}
                    Doc::Append(ref ldoc, ref rdoc) => {
                        doc = append_docs2(ldoc, rdoc, |doc| fcmds.push(doc));
                        continue;
                    }
                    // Newlines inside the group makes it not fit, but those outside lets it
                    // fit on the current line
                    Doc::Line => return newline_fits(mode),
                    Doc::BorrowedText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::OwnedText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::SmallText(ref str) => {
                        pos += str.len();
                        if pos > width {
                            return false;
                        }
                    }
                    Doc::FlatAlt(ref b, ref f) => {
                        doc = match mode {
                            Mode::Break => b,
                            Mode::Flat => f,
                        };
                        continue;
                    }

                    Doc::Column(ref f) => {
                        doc = temp_arena.alloc(f(pos));
                        continue;
                    }
                    Doc::Nesting(ref f) => {
                        doc = temp_arena.alloc(f(ind));
                        continue;
                    }
                    Doc::Nest(_, ref next)
                    | Doc::Group(ref next)
                    | Doc::Annotated(_, ref next)
                    | Doc::Union(_, ref next) => {
                        doc = next;
                        continue;
                    }
                }
                break;
            }
        }
    }

    let temp_arena = typed_arena::Arena::new();

    let mut pos = 0;
    let mut bcmds = vec![(0, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some(mut cmd) = bcmds.pop() {
        loop {
            let (ind, mode, doc) = cmd;
            match *doc {
                Doc::Nil => {}
                Doc::Append(ref ldoc, ref rdoc) => {
                    cmd.2 = append_docs2(ldoc, rdoc, |doc| bcmds.push((ind, mode, doc)));
                    continue;
                }
                Doc::FlatAlt(ref b, ref f) => {
                    cmd.2 = match mode {
                        Mode::Break => b,
                        Mode::Flat => f,
                    };
                    continue;
                }
                Doc::Group(ref doc) => match mode {
                    Mode::Flat => {
                        cmd = (ind, Mode::Flat, doc);
                        continue;
                    }
                    Mode::Break => {
                        cmd = if fitting(
                            &temp_arena,
                            doc,
                            &bcmds,
                            &mut fcmds,
                            pos,
                            width,
                            ind,
                            |mode| mode == Mode::Break,
                        ) {
                            (ind, Mode::Flat, &**doc)
                        } else {
                            (ind, Mode::Break, doc)
                        };
                        continue;
                    }
                },
                Doc::Nest(off, ref doc) => {
                    cmd = ((ind as isize).saturating_add(off) as usize, mode, doc);
                    continue;
                }
                Doc::Line => {
                    write_newline(ind, out)?;
                    pos = ind;
                }
                Doc::OwnedText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::BorrowedText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::SmallText(ref s) => {
                    out.write_str_all(s)?;
                    pos += s.len();
                }
                Doc::Annotated(ref ann, ref doc) => {
                    out.push_annotation(ann)?;
                    annotation_levels.push(bcmds.len());
                    cmd.2 = doc;
                    continue;
                }
                Doc::Union(ref l, ref r) => {
                    cmd.2 = if fitting(&temp_arena, l, &bcmds, &mut fcmds, pos, width, ind, |_| {
                        true
                    }) {
                        l
                    } else {
                        r
                    };
                    continue;
                }
                Doc::Column(ref f) => {
                    cmd.2 = temp_arena.alloc(f(pos));
                    continue;
                }
                Doc::Nesting(ref f) => {
                    cmd.2 = temp_arena.alloc(f(ind));
                    continue;
                }
            }

            break;
        }
        while annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            out.pop_annotation()?;
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>/*
    Contribuidores
		- Heitor582
        - Dromedario de Chapéu

    Fatorial é uma função matematica que consistem em realizar
	a multiplicação de todos os antecessores de um numero.

	Ex: 5! = 5 * 4 * 3 * 2 * 1 = 120
*/

// Para realizar uma fatoração com recursão basta fazer o retorno
// de uma função ser valor * a propia função recebendo valor - 1
fn fatorial(valor: usize) -> usize {
	// Para que não chege a multiplicar por 0 quando chegamos a 1 ou 0
	// é retornado 1 para que o utlimo valor sejá multilpicado por 1
    if valor <= 1 {
        return 1;
    }
    return valor * (fatorial(valor - 1));
}

fn main() {
    println!("{}", fatorial(10));
}

#[cfg(test)]
mod test {
    use super::*;
    fn teste_fatorial() {
        assert_eq!(fatorial(0), 1);
        assert_eq!(fatorial(1), 1);
        assert_eq!(fatorial(10), 3628800);
    }
}<commit_msg>Corrigindo fatorial recursivo<commit_after>/*
    Contribuidores
		- Heitor582
        - Dromedario de Chapéu

    Fatorial é uma função matematica que consistem em realizar
	a multiplicação de todos os antecessores de um numero.

	Ex: 5! = 5 * 4 * 3 * 2 * 1 = 120
*/

// Para realizar uma fatoração com recursão basta fazer o retorno
// de uma função ser valor * a propia função recebendo valor - 1
fn fatorial(valor: u128) -> u128 {
	// Para que não chege a multiplicar por 0 quando chegamos a 1 ou 0
	// é retornado 1 para que o utlimo valor sejá multilpicado por 1
    match valor {
        0 | 1  => 1,
        2.. => valor * (fatorial(valor - 1)),
    }
}

fn main() {
    println!("{}", fatorial(21));
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn teste_fatorial() {
        assert_eq!(fatorial(0), 1);
        assert_eq!(fatorial(1), 1);
        assert_eq!(fatorial(10), 3628800);
    }
}<|endoftext|>


<commit_before>use std::sync::Arc;

use conduit::{Handler, HandlerResult, RequestExt};
use conduit_router::{RequestParams, RouteBuilder};

use crate::controllers::*;
use crate::util::errors::{std_error, AppError, NotFound};
use crate::util::EndpointResult;
use crate::{App, Env};

pub fn build_router(app: &App) -> R404 {
    let mut api_router = RouteBuilder::new();

    // Route used by both `cargo search` and the frontend
    api_router.get("/crates", C(krate::search::search));

    // Routes used by `cargo`
    api_router.put("/crates/new", C(krate::publish::publish));
    api_router.get("/crates/:crate_id/owners", C(krate::owners::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::owners::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::owners::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank::yank));
    api_router.put(
        "/crates/:crate_id/:version/unyank",
        C(version::yank::unyank),
    );
    api_router.get(
        "/crates/:crate_id/:version/download",
        C(version::downloads::download),
    );

    // Routes that appear to be unused
    api_router.get("/versions", C(version::deprecated::index));
    api_router.get("/versions/:version_id", C(version::deprecated::show_by_id));

    // Routes used by the frontend
    api_router.get("/crates/:crate_id", C(krate::metadata::show));
    api_router.get("/crates/:crate_id/:version", C(version::metadata::show));
    api_router.get(
        "/crates/:crate_id/:version/readme",
        C(krate::metadata::readme),
    );
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::metadata::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads::downloads),
    );
    api_router.get(
        "/crates/:crate_id/:version/authors",
        C(version::metadata::authors),
    );
    api_router.get(
        "/crates/:crate_id/downloads",
        C(krate::downloads::downloads),
    );
    api_router.get("/crates/:crate_id/versions", C(krate::metadata::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::follow::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::follow::following));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owners::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owners::owner_user));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::metadata::reverse_dependencies),
    );
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::other::show));
    api_router.put("/users/:user_id", C(user::me::update_user));
    api_router.get("/users/:user_id/stats", C(user::other::stats));
    api_router.get("/teams/:team_id", C(team::show_team));
    api_router.get("/me", C(user::me::me));
    api_router.get("/me/updates", C(user::me::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.put("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.put(
        "/me/crate_owner_invitations/:crate_id",
        C(crate_owner_invitation::handle_invite),
    );
    api_router.put(
        "/me/crate_owner_invitations/accept/:token",
        C(crate_owner_invitation::handle_invite_with_token),
    );
    api_router.put(
        "/me/email_notifications",
        C(user::me::update_email_notifications),
    );
    api_router.get("/summary", C(krate::metadata::summary));
    api_router.put("/confirm/:email_token", C(user::me::confirm_user_email));
    api_router.put(
        "/users/:user_id/resend",
        C(user::me::regenerate_token_and_send),
    );
    api_router.get("/site_metadata", C(site_metadata::show_deployed_sha));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(Arc::clone(&api_router)));
    router.put("/api/v1/*path", R(Arc::clone(&api_router)));
    router.post("/api/v1/*path", R(Arc::clone(&api_router)));
    router.head("/api/v1/*path", R(Arc::clone(&api_router)));
    router.delete("/api/v1/*path", R(api_router));

    // Session management
    router.get("/api/private/session/begin", C(user::session::begin));
    router.get(
        "/api/private/session/authorize",
        C(user::session::authorize),
    );
    router.delete("/api/private/session", C(user::session::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    if app.config.env == Env::Development {
        let s = conduit_git_http_backend::Serve("./tmp/index-bare".into());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(Arc::clone(&s)));
        router.post("/git/index/*path", R(s));
    }

    R404(router)
}

struct C(pub fn(&mut dyn RequestExt) -> EndpointResult);

impl Handler for C {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => Ok(resp),
            Err(e) => {
                if let Some(cause) = e.cause() {
                    req.log_metadata("cause", cause.to_string())
                };
                match e.response() {
                    Some(response) => Ok(response),
                    None => Err(std_error(e)),
                }
            }
        }
    }
}

struct R<H>(pub Arc<H>);

impl<H: Handler> Handler for R<H> {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        *req.path_mut() = req.params()["path"].to_string();
        let R(ref sub_router) = *self;
        sub_router.call(req)
    }
}

// Can't derive Debug because of RouteBuilder.
#[allow(missing_debug_implementations)]
pub struct R404(pub RouteBuilder);

impl Handler for R404 {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        let R404(ref router) = *self;
        match router.recognize(&req.method(), req.path()) {
            Ok(m) => {
                req.mut_extensions().insert(m.params().clone());
                m.handler().call(req)
            }
            Err(..) => Ok(NotFound.into()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::errors::{
        bad_request, cargo_err, forbidden, internal, not_found, AppError, ChainError,
    };
    use crate::util::EndpointResult;

    use conduit::StatusCode;
    use conduit_test::MockRequest;
    use diesel::result::Error as DieselError;

    fn err<E: AppError>(err: E) -> EndpointResult {
        Err(Box::new(err))
    }

    #[test]
    fn http_error_responses() {
        let mut req = MockRequest::new(::conduit::Method::GET, "/");

        // Types for handling common error status codes
        assert_eq!(
            C(|_| Err(bad_request(""))).call(&mut req).unwrap().status(),
            StatusCode::BAD_REQUEST
        );
        assert_eq!(
            C(|_| Err(forbidden())).call(&mut req).unwrap().status(),
            StatusCode::FORBIDDEN
        );
        assert_eq!(
            C(|_| Err(DieselError::NotFound.into()))
                .call(&mut req)
                .unwrap()
                .status(),
            StatusCode::NOT_FOUND
        );
        assert_eq!(
            C(|_| Err(not_found())).call(&mut req).unwrap().status(),
            StatusCode::NOT_FOUND
        );

        // cargo_err errors are returned as 200 so that cargo displays this nicely on the command line
        assert_eq!(
            C(|_| Err(cargo_err(""))).call(&mut req).unwrap().status(),
            StatusCode::OK
        );

        // Inner errors are captured for logging when wrapped by a user facing error
        let response = C(|_| {
            Err("-1"
                .parse::<u8>()
                .chain_error(|| internal("middle error"))
                .chain_error(|| bad_request("outer user facing error"))
                .unwrap_err())
        })
        .call(&mut req)
        .unwrap();
        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
        assert_eq!(
            crate::middleware::log_request::get_log_message(&req, "cause"),
            "middle error caused by invalid digit found in string"
        );

        // All other error types are propogated up the middleware, eventually becoming status 500
        assert!(C(|_| Err(internal(""))).call(&mut req).is_err());
        assert!(
            C(|_| err::<::serde_json::Error>(::serde::de::Error::custom("ExpectedColon")))
                .call(&mut req)
                .is_err()
        );
        assert!(
            C(|_| err(::std::io::Error::new(::std::io::ErrorKind::Other, "")))
                .call(&mut req)
                .is_err()
        );
    }
}
<commit_msg>Add test to ensure the router returns a not found error<commit_after>use std::sync::Arc;

use conduit::{Handler, HandlerResult, RequestExt};
use conduit_router::{RequestParams, RouteBuilder};

use crate::controllers::*;
use crate::util::errors::{std_error, AppError, NotFound};
use crate::util::EndpointResult;
use crate::{App, Env};

pub fn build_router(app: &App) -> R404 {
    let mut api_router = RouteBuilder::new();

    // Route used by both `cargo search` and the frontend
    api_router.get("/crates", C(krate::search::search));

    // Routes used by `cargo`
    api_router.put("/crates/new", C(krate::publish::publish));
    api_router.get("/crates/:crate_id/owners", C(krate::owners::owners));
    api_router.put("/crates/:crate_id/owners", C(krate::owners::add_owners));
    api_router.delete("/crates/:crate_id/owners", C(krate::owners::remove_owners));
    api_router.delete("/crates/:crate_id/:version/yank", C(version::yank::yank));
    api_router.put(
        "/crates/:crate_id/:version/unyank",
        C(version::yank::unyank),
    );
    api_router.get(
        "/crates/:crate_id/:version/download",
        C(version::downloads::download),
    );

    // Routes that appear to be unused
    api_router.get("/versions", C(version::deprecated::index));
    api_router.get("/versions/:version_id", C(version::deprecated::show_by_id));

    // Routes used by the frontend
    api_router.get("/crates/:crate_id", C(krate::metadata::show));
    api_router.get("/crates/:crate_id/:version", C(version::metadata::show));
    api_router.get(
        "/crates/:crate_id/:version/readme",
        C(krate::metadata::readme),
    );
    api_router.get(
        "/crates/:crate_id/:version/dependencies",
        C(version::metadata::dependencies),
    );
    api_router.get(
        "/crates/:crate_id/:version/downloads",
        C(version::downloads::downloads),
    );
    api_router.get(
        "/crates/:crate_id/:version/authors",
        C(version::metadata::authors),
    );
    api_router.get(
        "/crates/:crate_id/downloads",
        C(krate::downloads::downloads),
    );
    api_router.get("/crates/:crate_id/versions", C(krate::metadata::versions));
    api_router.put("/crates/:crate_id/follow", C(krate::follow::follow));
    api_router.delete("/crates/:crate_id/follow", C(krate::follow::unfollow));
    api_router.get("/crates/:crate_id/following", C(krate::follow::following));
    api_router.get("/crates/:crate_id/owner_team", C(krate::owners::owner_team));
    api_router.get("/crates/:crate_id/owner_user", C(krate::owners::owner_user));
    api_router.get(
        "/crates/:crate_id/reverse_dependencies",
        C(krate::metadata::reverse_dependencies),
    );
    api_router.get("/keywords", C(keyword::index));
    api_router.get("/keywords/:keyword_id", C(keyword::show));
    api_router.get("/categories", C(category::index));
    api_router.get("/categories/:category_id", C(category::show));
    api_router.get("/category_slugs", C(category::slugs));
    api_router.get("/users/:user_id", C(user::other::show));
    api_router.put("/users/:user_id", C(user::me::update_user));
    api_router.get("/users/:user_id/stats", C(user::other::stats));
    api_router.get("/teams/:team_id", C(team::show_team));
    api_router.get("/me", C(user::me::me));
    api_router.get("/me/updates", C(user::me::updates));
    api_router.get("/me/tokens", C(token::list));
    api_router.put("/me/tokens", C(token::new));
    api_router.delete("/me/tokens/:id", C(token::revoke));
    api_router.get(
        "/me/crate_owner_invitations",
        C(crate_owner_invitation::list),
    );
    api_router.put(
        "/me/crate_owner_invitations/:crate_id",
        C(crate_owner_invitation::handle_invite),
    );
    api_router.put(
        "/me/crate_owner_invitations/accept/:token",
        C(crate_owner_invitation::handle_invite_with_token),
    );
    api_router.put(
        "/me/email_notifications",
        C(user::me::update_email_notifications),
    );
    api_router.get("/summary", C(krate::metadata::summary));
    api_router.put("/confirm/:email_token", C(user::me::confirm_user_email));
    api_router.put(
        "/users/:user_id/resend",
        C(user::me::regenerate_token_and_send),
    );
    api_router.get("/site_metadata", C(site_metadata::show_deployed_sha));
    let api_router = Arc::new(R404(api_router));

    let mut router = RouteBuilder::new();

    // Mount the router under the /api/v1 path so we're at least somewhat at the
    // liberty to change things in the future!
    router.get("/api/v1/*path", R(Arc::clone(&api_router)));
    router.put("/api/v1/*path", R(Arc::clone(&api_router)));
    router.post("/api/v1/*path", R(Arc::clone(&api_router)));
    router.head("/api/v1/*path", R(Arc::clone(&api_router)));
    router.delete("/api/v1/*path", R(api_router));

    // Session management
    router.get("/api/private/session/begin", C(user::session::begin));
    router.get(
        "/api/private/session/authorize",
        C(user::session::authorize),
    );
    router.delete("/api/private/session", C(user::session::logout));

    // Only serve the local checkout of the git index in development mode.
    // In production, for crates.io, cargo gets the index from
    // https://github.com/rust-lang/crates.io-index directly.
    if app.config.env == Env::Development {
        let s = conduit_git_http_backend::Serve("./tmp/index-bare".into());
        let s = Arc::new(s);
        router.get("/git/index/*path", R(Arc::clone(&s)));
        router.post("/git/index/*path", R(s));
    }

    R404(router)
}

struct C(pub fn(&mut dyn RequestExt) -> EndpointResult);

impl Handler for C {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        let C(f) = *self;
        match f(req) {
            Ok(resp) => Ok(resp),
            Err(e) => {
                if let Some(cause) = e.cause() {
                    req.log_metadata("cause", cause.to_string())
                };
                match e.response() {
                    Some(response) => Ok(response),
                    None => Err(std_error(e)),
                }
            }
        }
    }
}

struct R<H>(pub Arc<H>);

impl<H: Handler> Handler for R<H> {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        *req.path_mut() = req.params()["path"].to_string();
        let R(ref sub_router) = *self;
        sub_router.call(req)
    }
}

// Can't derive Debug because of RouteBuilder.
#[allow(missing_debug_implementations)]
pub struct R404(pub RouteBuilder);

impl Handler for R404 {
    fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
        let R404(ref router) = *self;
        match router.recognize(&req.method(), req.path()) {
            Ok(m) => {
                req.mut_extensions().insert(m.params().clone());
                m.handler().call(req)
            }
            Err(..) => Ok(NotFound.into()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::errors::{
        bad_request, cargo_err, forbidden, internal, not_found, AppError, ChainError,
    };
    use crate::util::EndpointResult;

    use conduit::{Body, StatusCode};
    use conduit_test::MockRequest;
    use diesel::result::Error as DieselError;

    fn err<E: AppError>(err: E) -> EndpointResult {
        Err(Box::new(err))
    }

    #[test]
    fn http_error_responses() {
        let mut req = MockRequest::new(::conduit::Method::GET, "/");

        // Types for handling common error status codes
        assert_eq!(
            C(|_| Err(bad_request(""))).call(&mut req).unwrap().status(),
            StatusCode::BAD_REQUEST
        );
        assert_eq!(
            C(|_| Err(forbidden())).call(&mut req).unwrap().status(),
            StatusCode::FORBIDDEN
        );
        assert_eq!(
            C(|_| Err(DieselError::NotFound.into()))
                .call(&mut req)
                .unwrap()
                .status(),
            StatusCode::NOT_FOUND
        );
        assert_eq!(
            C(|_| Err(not_found())).call(&mut req).unwrap().status(),
            StatusCode::NOT_FOUND
        );

        // cargo_err errors are returned as 200 so that cargo displays this nicely on the command line
        assert_eq!(
            C(|_| Err(cargo_err(""))).call(&mut req).unwrap().status(),
            StatusCode::OK
        );

        // Inner errors are captured for logging when wrapped by a user facing error
        let response = C(|_| {
            Err("-1"
                .parse::<u8>()
                .chain_error(|| internal("middle error"))
                .chain_error(|| bad_request("outer user facing error"))
                .unwrap_err())
        })
        .call(&mut req)
        .unwrap();
        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
        assert_eq!(
            crate::middleware::log_request::get_log_message(&req, "cause"),
            "middle error caused by invalid digit found in string"
        );

        // All other error types are propogated up the middleware, eventually becoming status 500
        assert!(C(|_| Err(internal(""))).call(&mut req).is_err());
        assert!(
            C(|_| err::<::serde_json::Error>(::serde::de::Error::custom("ExpectedColon")))
                .call(&mut req)
                .is_err()
        );
        assert!(
            C(|_| err(::std::io::Error::new(::std::io::ErrorKind::Other, "")))
                .call(&mut req)
                .is_err()
        );
    }

    #[test]
    fn router_produces_json_not_found() {
        let route_builder = RouteBuilder::new();
        let mut req = MockRequest::new(::conduit::Method::GET, "/");

        let (parts, body) = R404(route_builder).call(&mut req).unwrap().into_parts();
        assert_eq!(parts.status, StatusCode::NOT_FOUND);
        assert!(matches!(
            body,
            Body::Owned(vec) if vec == br#"{"errors":[{"detail":"Not Found"}]}"#
        ));
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::Write;

use bufstream::BufStream;

use common::consts;
use protocol::cmd::Cmd;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;
use protocol::cmd::SetInstr;

use super::conversions::as_number;
use super::conversions::as_string;
use super::errors::TcpTransportError;
use super::stats::TransportStats;
use super::typedefs::TcpTransportResult;

pub struct TcpTransport<T: Read + Write> {
    stream: BufStream<T>,

    pub line_buffer: Vec<u8>,
    pub line_cursor: usize,
    pub line_break_pos: usize,

    key_maxlen: u64,

    stats: TransportStats,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard

            // Used to read the first line of a command, which includes a
            // keyword, a key, flags and a bytecount. We don't expect it to be
            // much longer than the key itself. If it is we panic...
            line_buffer: vec![0; 250 + 100],
            line_cursor: 0,
            line_break_pos: 0,

            stats: TransportStats::new(),
            stream: BufStream::new(stream),
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_stats_clone(&self) -> TransportStats {
        self.stats.clone()
    }

    pub fn get_stream(&self) -> &T {
        self.stream.get_ref()
    }

    // Basic bytes manipulation and reading from the stream

    pub fn read_byte(&mut self) -> TcpTransportResult<u8> {
        let mut bytes = [0; 1];

        match self.stream.read(&mut bytes) {
            Ok(1) => {
                // Update stats
                self.stats.bytes_read += 1;

                Ok(bytes[0])
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn read_bytes(&mut self, len: u64) -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![0; len as usize];

        match self.stream.read(&mut bytes[..]) {
            Ok(n) => {
                // Update stats
                self.stats.bytes_read += n as u64;

                if (n as u64) < len {
                    bytes.truncate(n);
                }

                Ok(bytes)
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn preread_line(&mut self) -> TcpTransportResult<()> {
        let mut cursor = 0;

        // We keep reading one byte at a time into line_buffer, looking for
        // a line terminator \r\n. The underlying stream is buffered.
        loop {
            // Make sure we don't go over bounds
            if cursor >= self.line_buffer.len() {
                panic!("Line too long to be read");
            }

            let rv = self.stream
                         .read(&mut self.line_buffer[cursor..cursor + 1]);

            // If there was an error or if there was nothing to read we bail
            if rv.is_err() || rv.unwrap() == 0 {
                return Err(TcpTransportError::StreamReadError);
            }

            // Update stats
            self.stats.bytes_read += 1;

            // We found \r
            if self.line_buffer[cursor] == consts::BYTE_CARRIAGE_RETURN {
                // Read one more, hoping it's \n
                cursor += 1;
                let rv = self.stream
                             .read(&mut self.line_buffer[cursor..cursor + 1]);

                // If there was an error or if there was nothing to read we bail
                if rv.is_err() || rv.unwrap() == 0 {
                    return Err(TcpTransportError::StreamReadError);
                }

                // Update stats
                self.stats.bytes_read += 1;

                // Woops, it's not \n, we bail
                if self.line_buffer[cursor] != consts::BYTE_LINE_FEED {
                    return Err(TcpTransportError::StreamReadError);
                }

                break;
            }

            cursor += 1;
        }

        self.line_cursor = 0;
        self.line_break_pos = cursor - 1;  // point it at \r, not \n

        Ok(())
    }

    pub fn line_is_empty(&self) -> bool {
        // If the cursor precedes the line break then there are still bytes to
        // read from the line, otherwise it's empty
        self.line_cursor >= self.line_break_pos
    }

    pub fn line_remove_first_char(&mut self) -> TcpTransportResult<()> {
        match !self.line_is_empty() {
            true => {
                self.line_cursor += 1;
                Ok(())
            }
            false => Err(TcpTransportError::LineReadError),
        }
    }

    pub fn line_parse_word(&mut self) -> TcpTransportResult<&[u8]> {
        // If the very first char is a space then our caller is out of sync
        if self.line_buffer[self.line_cursor] == 32 {
            return Err(TcpTransportError::StreamReadError);
        }

        let mut space_idx = 0;
        let mut found = false;

        for i in self.line_cursor + 1..self.line_break_pos {
            // We found a space
            if self.line_buffer[i] == consts::BYTE_SPACE {
                space_idx = i;
                found = true;
                break;
            }
        }

        // If we didn't find a space then the whole line is a word
        if !found {
            space_idx = self.line_break_pos;
        }

        // Advance the cursor, we've now "consumed" the word we found
        let word = &self.line_buffer[self.line_cursor..space_idx];
        self.line_cursor = space_idx;

        Ok(word)
    }

    // Writing to the stream

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        match self.stream.flush() {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        match self.stream.write(bytes) {
            Ok(cnt_written) => {
                // Update stats
                self.stats.bytes_written += cnt_written as u64;

                Ok(cnt_written)
            }
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    // Parse individual commands

    pub fn parse_cmd_get(&mut self) -> TcpTransportResult<Cmd> {
        // remove the space after the keyword
        try!(self.line_remove_first_char());

        // parse the key
        let key_str = {
            let key = try!(self.line_parse_word());
            try!(as_string(key))
        };

        // We expect to find the end of the line now
        if self.line_is_empty() {
            Ok(Cmd::Get(Get { key: key_str }))
        } else {
            Err(TcpTransportError::CommandParseError)
        }
    }

    pub fn parse_cmd_set(&mut self) -> TcpTransportResult<Cmd> {
        // remove the space after the keyword
        try!(self.line_remove_first_char());

        // parse the key + remove trailing space
        let key_str = {
            let key = try!(self.line_parse_word());
            try!(as_string(key))
        };
        try!(self.line_remove_first_char());

        // parse the flags + remove trailing space
        let flags_num = {
            let flags = try!(self.line_parse_word());
            try!(as_number::<u16>(flags))
        };
        try!(self.line_remove_first_char());

        // parse the exptime + remove trailing space
        let exptime_num = {
            let exptime = try!(self.line_parse_word());
            try!(as_number::<u32>(exptime))
        };
        try!(self.line_remove_first_char());

        // parse the bytelen + remove trailing space
        let bytelen_num = {
            let bytelen = try!(self.line_parse_word());
            try!(as_number::<u64>(bytelen))
        };
        try!(self.line_remove_first_char());

        // parse noreply
        let noreply_flag = {
            let noreply = try!(self.line_parse_word());
            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        // We know the byte length, so now read the value
        let value = try!(self.read_bytes(bytelen_num));

        // Read the line termination marker and verify it
        let newline = try!(self.read_bytes(2));
        if !newline.starts_with(&[consts::BYTE_CARRIAGE_RETURN,
                                  consts::BYTE_LINE_FEED]) {
            return Err(TcpTransportError::CommandParseError);
        }

        // We got all the values we expected and there is nothing left
        return Ok(Cmd::Set(Set {
            instr: SetInstr::Set,
            key: key_str,
            flags: flags_num,
            exptime: exptime_num,
            data: value,
            noreply: noreply_flag,
        }));
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        // read the first line
        try!(self.preread_line());

        let keyword_str = {
            let keyword = try!(self.line_parse_word());
            try!(as_string(keyword))
        };

        if keyword_str == "get" {
            return self.parse_cmd_get();
        } else if keyword_str == "set" {
            return self.parse_cmd_set();
        } else if keyword_str == "stats" {
            return Ok(Cmd::Stats);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Empty => (),
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string("STAT "));
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Value(ref value) => {
                try!(self.write_string("VALUE ")); // keyword
                try!(self.write_string(&value.key)); // key
                try!(self.write_string(" ")); // space
                try!(self.write_string(&value.flags.to_string())); // flags
                try!(self.write_string(" ")); // space
                try!(self.write_string(&value.data.len().to_string())); // bytelen
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_bytes(&value.data)); // data block
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_string(&"END\r\n".to_string())); // END + newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<commit_msg>add comment<commit_after>use std::io::Read;
use std::io::Write;

use bufstream::BufStream;

use common::consts;
use protocol::cmd::Cmd;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;
use protocol::cmd::SetInstr;

use super::conversions::as_number;
use super::conversions::as_string;
use super::errors::TcpTransportError;
use super::stats::TransportStats;
use super::typedefs::TcpTransportResult;

pub struct TcpTransport<T: Read + Write> {
    stream: BufStream<T>,

    pub line_buffer: Vec<u8>,
    pub line_cursor: usize,
    pub line_break_pos: usize,

    key_maxlen: u64,

    stats: TransportStats,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard

            // Used to read the first line of a command, which includes a
            // keyword, a key, flags and a bytecount. We don't expect it to be
            // much longer than the key itself. If it is we panic...
            line_buffer: vec![0; 250 + 100],
            line_cursor: 0,
            line_break_pos: 0,

            stats: TransportStats::new(),
            stream: BufStream::new(stream),
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_stats_clone(&self) -> TransportStats {
        self.stats.clone()
    }

    pub fn get_stream(&self) -> &T {
        self.stream.get_ref()
    }

    // Basic bytes manipulation and reading from the stream

    pub fn read_byte(&mut self) -> TcpTransportResult<u8> {
        let mut bytes = [0; 1];

        match self.stream.read(&mut bytes) {
            Ok(1) => {
                // Update stats
                self.stats.bytes_read += 1;

                Ok(bytes[0])
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn read_bytes(&mut self, len: u64) -> TcpTransportResult<Vec<u8>> {
        // TODO: test large values >4kb
        let mut bytes = vec![0; len as usize];

        match self.stream.read(&mut bytes[..]) {
            Ok(n) => {
                // Update stats
                self.stats.bytes_read += n as u64;

                if (n as u64) < len {
                    bytes.truncate(n);
                }

                Ok(bytes)
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn preread_line(&mut self) -> TcpTransportResult<()> {
        let mut cursor = 0;

        // We keep reading one byte at a time into line_buffer, looking for
        // a line terminator \r\n. The underlying stream is buffered.
        loop {
            // Make sure we don't go over bounds
            if cursor >= self.line_buffer.len() {
                panic!("Line too long to be read");
            }

            let rv = self.stream
                         .read(&mut self.line_buffer[cursor..cursor + 1]);

            // If there was an error or if there was nothing to read we bail
            if rv.is_err() || rv.unwrap() == 0 {
                return Err(TcpTransportError::StreamReadError);
            }

            // Update stats
            self.stats.bytes_read += 1;

            // We found \r
            if self.line_buffer[cursor] == consts::BYTE_CARRIAGE_RETURN {
                // Read one more, hoping it's \n
                cursor += 1;
                let rv = self.stream
                             .read(&mut self.line_buffer[cursor..cursor + 1]);

                // If there was an error or if there was nothing to read we bail
                if rv.is_err() || rv.unwrap() == 0 {
                    return Err(TcpTransportError::StreamReadError);
                }

                // Update stats
                self.stats.bytes_read += 1;

                // Woops, it's not \n, we bail
                if self.line_buffer[cursor] != consts::BYTE_LINE_FEED {
                    return Err(TcpTransportError::StreamReadError);
                }

                break;
            }

            cursor += 1;
        }

        self.line_cursor = 0;
        self.line_break_pos = cursor - 1;  // point it at \r, not \n

        Ok(())
    }

    pub fn line_is_empty(&self) -> bool {
        // If the cursor precedes the line break then there are still bytes to
        // read from the line, otherwise it's empty
        self.line_cursor >= self.line_break_pos
    }

    pub fn line_remove_first_char(&mut self) -> TcpTransportResult<()> {
        match !self.line_is_empty() {
            true => {
                self.line_cursor += 1;
                Ok(())
            }
            false => Err(TcpTransportError::LineReadError),
        }
    }

    pub fn line_parse_word(&mut self) -> TcpTransportResult<&[u8]> {
        // If the very first char is a space then our caller is out of sync
        if self.line_buffer[self.line_cursor] == 32 {
            return Err(TcpTransportError::StreamReadError);
        }

        let mut space_idx = 0;
        let mut found = false;

        for i in self.line_cursor + 1..self.line_break_pos {
            // We found a space
            if self.line_buffer[i] == consts::BYTE_SPACE {
                space_idx = i;
                found = true;
                break;
            }
        }

        // If we didn't find a space then the whole line is a word
        if !found {
            space_idx = self.line_break_pos;
        }

        // Advance the cursor, we've now "consumed" the word we found
        let word = &self.line_buffer[self.line_cursor..space_idx];
        self.line_cursor = space_idx;

        Ok(word)
    }

    // Writing to the stream

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        match self.stream.flush() {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        match self.stream.write(bytes) {
            Ok(cnt_written) => {
                // Update stats
                self.stats.bytes_written += cnt_written as u64;

                Ok(cnt_written)
            }
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    // Parse individual commands

    pub fn parse_cmd_get(&mut self) -> TcpTransportResult<Cmd> {
        // remove the space after the keyword
        try!(self.line_remove_first_char());

        // parse the key
        let key_str = {
            let key = try!(self.line_parse_word());
            try!(as_string(key))
        };

        // We expect to find the end of the line now
        if self.line_is_empty() {
            Ok(Cmd::Get(Get { key: key_str }))
        } else {
            Err(TcpTransportError::CommandParseError)
        }
    }

    pub fn parse_cmd_set(&mut self) -> TcpTransportResult<Cmd> {
        // remove the space after the keyword
        try!(self.line_remove_first_char());

        // parse the key + remove trailing space
        let key_str = {
            let key = try!(self.line_parse_word());
            try!(as_string(key))
        };
        try!(self.line_remove_first_char());

        // parse the flags + remove trailing space
        let flags_num = {
            let flags = try!(self.line_parse_word());
            try!(as_number::<u16>(flags))
        };
        try!(self.line_remove_first_char());

        // parse the exptime + remove trailing space
        let exptime_num = {
            let exptime = try!(self.line_parse_word());
            try!(as_number::<u32>(exptime))
        };
        try!(self.line_remove_first_char());

        // parse the bytelen + remove trailing space
        let bytelen_num = {
            let bytelen = try!(self.line_parse_word());
            try!(as_number::<u64>(bytelen))
        };
        try!(self.line_remove_first_char());

        // parse noreply
        let noreply_flag = {
            let noreply = try!(self.line_parse_word());
            let noreply_str = try!(as_string(noreply));
            match noreply_str == "noreply" {
                true => true,
                false => false,
            }
        };

        // We know the byte length, so now read the value
        let value = try!(self.read_bytes(bytelen_num));

        // Read the line termination marker and verify it
        let newline = try!(self.read_bytes(2));
        if !newline.starts_with(&[consts::BYTE_CARRIAGE_RETURN,
                                  consts::BYTE_LINE_FEED]) {
            return Err(TcpTransportError::CommandParseError);
        }

        // We got all the values we expected and there is nothing left
        return Ok(Cmd::Set(Set {
            instr: SetInstr::Set,
            key: key_str,
            flags: flags_num,
            exptime: exptime_num,
            data: value,
            noreply: noreply_flag,
        }));
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        // read the first line
        try!(self.preread_line());

        let keyword_str = {
            let keyword = try!(self.line_parse_word());
            try!(as_string(keyword))
        };

        if keyword_str == "get" {
            return self.parse_cmd_get();
        } else if keyword_str == "set" {
            return self.parse_cmd_set();
        } else if keyword_str == "stats" {
            return Ok(Cmd::Stats);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Empty => (),
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string("STAT "));
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Value(ref value) => {
                try!(self.write_string("VALUE ")); // keyword
                try!(self.write_string(&value.key)); // key
                try!(self.write_string(" ")); // space
                try!(self.write_string(&value.flags.to_string())); // flags
                try!(self.write_string(" ")); // space
                try!(self.write_string(&value.data.len().to_string())); // bytelen
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_bytes(&value.data)); // data block
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_string(&"END\r\n".to_string())); // END + newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<|endoftext|>


<commit_before>extern crate glib;
use glib::Cast;

extern crate gstreamer as gst;
use gstreamer::{TagSetterExt, Toc, TocEntry, TocEntryType, TocScope, TocSetterExt};

use std::collections::HashMap;

use std::i64;

use super::{Chapter, Exporter};

static EXTENSION: &'static str = "toc.mkv";
static AUDIO_EXTENSION: &'static str = "toc.mka";

pub struct MatroskaTocFormat {
}

impl MatroskaTocFormat {
    pub fn get_extension() -> &'static str {
        &EXTENSION
    }

    pub fn get_audio_extension() -> &'static str {
        &AUDIO_EXTENSION
    }

    pub fn new() -> Self {
        MatroskaTocFormat{}
    }
}

impl Exporter for MatroskaTocFormat {
    fn export(&self,
        metadata: &HashMap<String, String>,
        chapters: &[Chapter],
        destination: &gst::Element,
    ) {
        {
            let tag_setter =
                destination.clone().dynamic_cast::<gst::TagSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TagSetter");

            let mut tag_list = gst::TagList::new();
            {
                let tag_list = tag_list.get_mut().unwrap();
                if let Some(title) = metadata.get(super::METADATA_TITLE) {
                    tag_list.add::<gst::tags::Title>(title, gst::TagMergeMode::Append);
                }
                if let Some(artist) = metadata.get(super::METADATA_ARTIST) {
                    tag_list.add::<gst::tags::Artist>(artist, gst::TagMergeMode::Append);
                }
            }

            println!("**** SETTING Tags");
            tag_setter.merge_tags(&tag_list, gst::TagMergeMode::Append)
        }

        {
            let toc_setter =
                destination.clone().dynamic_cast::<gst::TocSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TocSetter");

            toc_setter.reset();

            let mut toc = Toc::new(TocScope::Global);
            let mut toc_entry = TocEntry::new(TocEntryType::Edition, "00");
            {
                let toc_entry = toc_entry.get_mut().unwrap();

                let mut min_pos = i64::MAX;
                let mut max_pos = 0i64;

                for (index, chapter) in chapters.iter().enumerate() {
                    let mut toc_sub_entry = TocEntry::new(
                        TocEntryType::Chapter,
                        &format!("{:02}", index + 1),
                    );

                    let mut tag_list = gst::TagList::new();
                    tag_list.get_mut().unwrap()
                        .add::<gst::tags::Title>(
                            &chapter.get_title().unwrap_or(super::DEFAULT_TITLE).to_owned(),
                            gst::TagMergeMode::Append,
                        );
                    toc_sub_entry.get_mut().unwrap()
                        .set_tags(tag_list);

                    let start = chapter.start.nano_total as i64;
                    let end = chapter.end.nano_total as i64;
                    toc_sub_entry.get_mut().unwrap().set_start_stop_times(start, end);

                    if start < min_pos {
                        min_pos = start;
                    }
                    if end > max_pos {
                        max_pos = end;
                    }

                    toc_entry.append_sub_entry(toc_sub_entry);
                }

                toc_entry.set_start_stop_times(min_pos, max_pos);
            }

            toc.get_mut().unwrap().append_entry(toc_entry);

            println!("**** SETTING TOC");
            toc_setter.set_toc(&toc);
        }
    }
}
<commit_msg>Matroska export: minor cleanups (#56)<commit_after>extern crate glib;
use glib::Cast;

extern crate gstreamer as gst;
use gstreamer::{TagSetterExt, Toc, TocEntry, TocEntryType, TocScope, TocSetterExt};

use std::collections::HashMap;

use std::i64;

use super::{Chapter, Exporter};

static EXTENSION: &'static str = "toc.mkv";
static AUDIO_EXTENSION: &'static str = "toc.mka";

pub struct MatroskaTocFormat {
}

impl MatroskaTocFormat {
    pub fn get_extension() -> &'static str {
        &EXTENSION
    }

    pub fn get_audio_extension() -> &'static str {
        &AUDIO_EXTENSION
    }

    pub fn new() -> Self {
        MatroskaTocFormat{}
    }
}

impl Exporter for MatroskaTocFormat {
    fn export(&self,
        metadata: &HashMap<String, String>,
        chapters: &[Chapter],
        destination: &gst::Element,
    ) {
        {
            let tag_setter =
                destination.clone().dynamic_cast::<gst::TagSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TagSetter");

            let mut tag_list = gst::TagList::new();
            {
                let tag_list = tag_list.get_mut().unwrap();
                if let Some(title) = metadata.get(super::METADATA_TITLE) {
                    tag_list.add::<gst::tags::Title>(title, gst::TagMergeMode::Append);
                }
                if let Some(artist) = metadata.get(super::METADATA_ARTIST) {
                    tag_list.add::<gst::tags::Artist>(artist, gst::TagMergeMode::Append);
                }
            }

            tag_setter.merge_tags(&tag_list, gst::TagMergeMode::Append)
        }

        {
            let toc_setter =
                destination.clone().dynamic_cast::<gst::TocSetter>()
                    .expect("MatroskaTocFormat::export muxer is not a TocSetter");

            toc_setter.reset();

            let mut toc = Toc::new(TocScope::Global);
            let mut toc_entry = TocEntry::new(TocEntryType::Edition, "00");
            {
                let toc_entry = toc_entry.get_mut().unwrap();

                let mut min_pos = i64::MAX;
                let mut max_pos = 0i64;

                for (index, chapter) in chapters.iter().enumerate() {
                    let mut toc_sub_entry = TocEntry::new(
                        TocEntryType::Chapter,
                        &format!("{:02}", index + 1),
                    );

                    let mut tag_list = gst::TagList::new();
                    tag_list.get_mut().unwrap()
                        .add::<gst::tags::Title>(
                            &chapter.get_title().unwrap_or(super::DEFAULT_TITLE).to_owned(),
                            gst::TagMergeMode::Append,
                        );
                    toc_sub_entry.get_mut().unwrap()
                        .set_tags(tag_list);

                    let start = chapter.start.nano_total as i64;
                    let end = chapter.end.nano_total as i64;
                    toc_sub_entry.get_mut().unwrap().set_start_stop_times(start, end);

                    if start < min_pos {
                        min_pos = start;
                    }
                    if end > max_pos {
                        max_pos = end;
                    }

                    toc_entry.append_sub_entry(toc_sub_entry);
                }
            }

            toc.get_mut().unwrap().append_entry(toc_entry);

            toc_setter.set_toc(&toc);
        }
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::Hash;

struct Visited<T> {
    hash_set: HashSet<T>
}

impl<T> Visited<T> where T: Hash + Clone + Eq {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new()
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        self.hash_set.insert(value.clone())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash + Clone + Eq;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut visited = Visited::new();
        let mut stack = vec![(self.expand(&start), None)];

        loop {
            let next = match stack.last_mut() {
                None => return None,
                Some(&mut (ref mut iter, _)) => iter.next()
            };
            if let Some((action, state)) = next {
                if !visited.insert(&state) {
                    continue;
                }
                if goal.is_goal(&state) {
                    return Some(
                        stack.into_iter()
                             .filter_map(|(_, a)| a)
                             .chain(Some(action).into_iter())
                             .collect()
                    )
                }
                stack.push((self.expand(&state), Some(action)));
            } else {
                stack.pop();
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<commit_msg>Remove redundant derefs<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::Hash;

struct Visited<T> {
    hash_set: HashSet<T>
}

impl<T> Visited<T> where T: Hash + Clone + Eq {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new()
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        self.hash_set.insert(value.clone())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        self == state
    }
}

pub trait SearchSpace {
    type State: Hash + Clone + Eq;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut visited = Visited::new();
        let mut stack = vec![(self.expand(&start), None)];

        loop {
            let next = match stack.last_mut() {
                None => return None,
                Some(&mut (ref mut iter, _)) => iter.next()
            };
            if let Some((action, state)) = next {
                if !visited.insert(&state) {
                    continue;
                }
                if goal.is_goal(&state) {
                    return Some(
                        stack.into_iter()
                             .filter_map(|(_, a)| a)
                             .chain(Some(action).into_iter())
                             .collect()
                    )
                }
                stack.push((self.expand(&state), Some(action)));
            } else {
                stack.pop();
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<|endoftext|>