<commit_before>use std::io::{self, Write};
use std::path::Path;
use std::sync::Arc;

use regex::bytes::Regex;
use term::{self, Terminal};
use term::color::*;
use term::terminfo::TermInfo;

use terminal::TerminfoTerminal;
use types::FileTypeDef;

use self::Writer::*;

/// Printer encapsulates all output logic for searching.
///
/// Note that we currently ignore all write errors. It's probably worthwhile
/// to fix this, but printers are only ever used for writes to stdout or
/// writes to memory, neither of which commonly fail.
pub struct Printer<W> {
    /// The underlying writer.
    wtr: Writer<W>,
    /// Whether anything has been printed to wtr yet.
    has_printed: bool,
    /// The string to use to separate non-contiguous runs of context lines.
    context_separator: Vec<u8>,
    /// The end-of-line terminator used by the printer. In general, eols are
    /// printed via the match directly, but occasionally we need to insert them
    /// ourselves (for example, to print a context separator).
    eol: u8,
    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    heading: bool,
    /// Whether to suppress all output.
    quiet: bool,
    /// A string to use as a replacement of each match in a matching line.
    replace: Option<Vec<u8>>,
    /// Whether to prefix each match with the corresponding file name.
    with_filename: bool,
}

impl<W: Send + io::Write> Printer<W> {
    /// Create a new printer that writes to wtr.
    ///
    /// `color` should be true if the printer should try to use coloring.
    pub fn new(wtr: W, color: bool) -> Printer<W> {
        Printer {
            wtr: Writer::new(wtr, color),
            has_printed: false,
            context_separator: "--".to_string().into_bytes(),
            eol: b'\n',
            heading: false,
            quiet: false,
            replace: None,
            with_filename: false,
        }
    }

    /// Set the context separator. The default is `--`.
    pub fn context_separator(mut self, sep: Vec<u8>) -> Printer<W> {
        self.context_separator = sep;
        self
    }

    /// Set the end-of-line terminator. The default is `\n`.
    pub fn eol(mut self, eol: u8) -> Printer<W> {
        self.eol = eol;
        self
    }

    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    pub fn heading(mut self, yes: bool) -> Printer<W> {
        self.heading = yes;
        self
    }

    /// When set, all output is suppressed.
    pub fn quiet(mut self, yes: bool) -> Printer<W> {
        self.quiet = yes;
        self
    }

    /// Replace every match in each matching line with the replacement string
    /// given.
    ///
    /// The replacement string syntax is documented here:
    /// https://doc.rust-lang.org/regex/regex/bytes/struct.Captures.html#method.expand
    pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> {
        self.replace = Some(replacement);
        self
    }

    /// When set, each match is prefixed with the file name that it came from.
    pub fn with_filename(mut self, yes: bool) -> Printer<W> {
        self.with_filename = yes;
        self
    }

    /// Returns true if and only if something has been printed.
    pub fn has_printed(&self) -> bool {
        self.has_printed
    }

    /// Flushes the underlying writer and returns it.
    pub fn into_inner(mut self) -> W {
        let _ = self.wtr.flush();
        self.wtr.into_inner()
    }

    /// Prints a type definition.
    pub fn type_def(&mut self, def: &FileTypeDef) {
        self.write(def.name().as_bytes());
        self.write(b": ");
        let mut first = true;
        for pat in def.patterns() {
            if !first {
                self.write(b", ");
            }
            self.write(pat.as_bytes());
            first = false;
        }
        self.write_eol();
    }

    /// Prints the given path.
    pub fn path<P: AsRef<Path>>(&mut self, path: P) {
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
    }

    /// Prints the given path and a count of the number of matches found.
    pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
        if self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        self.write(count.to_string().as_bytes());
        self.write_eol();
    }

    /// Prints the context separator.
    pub fn context_separate(&mut self) {
        // N.B. We can't use `write` here because of borrowing restrictions.
        if self.quiet {
            return;
        }
        if self.context_separator.is_empty() {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(&self.context_separator);
        let _ = self.wtr.write_all(&[self.eol]);
    }

    pub fn matched<P: AsRef<Path>>(
        &mut self,
        re: &Regex,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b':');
        }
        if self.replace.is_some() {
            let line = re.replace_all(
                &buf[start..end], &**self.replace.as_ref().unwrap());
            self.write(&line);
        } else {
            self.write_match(re, &buf[start..end]);
        }
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
        if !self.wtr.is_color() {
            self.write(buf);
            return;
        }
        let mut last_written = 0;
        for (s, e) in re.find_iter(buf) {
            self.write(&buf[last_written..s]);
            let _ = self.wtr.fg(BRIGHT_RED);
            let _ = self.wtr.attr(term::Attr::Bold);
            self.write(&buf[s..e]);
            let _ = self.wtr.reset();
            last_written = e;
        }
        self.write(&buf[last_written..]);
    }

    pub fn context<P: AsRef<Path>>(
        &mut self,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b"-");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b'-');
        }
        self.write(&buf[start..end]);
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(GREEN);
        }
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
    }

    fn line_number(&mut self, n: u64, sep: u8) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(YELLOW);
            let _ = self.wtr.attr(term::Attr::Bold);
        }
        self.write(n.to_string().as_bytes());
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
        self.write(&[sep]);
    }

    fn write(&mut self, buf: &[u8]) {
        if self.quiet {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(buf);
    }

    fn write_eol(&mut self) {
        let eol = self.eol;
        self.write(&[eol]);
    }
}

enum Writer<W> {
    Colored(TerminfoTerminal<W>),
    NoColor(W),
}

lazy_static! {
    static ref TERMINFO: Option<Arc<TermInfo>> = {
        match term::terminfo::TermInfo::from_env() {
            Ok(info) => Some(Arc::new(info)),
            Err(err) => {
                debug!("error loading terminfo for coloring: {}", err);
                None
            }
        }
    };
}

impl<W: Send + io::Write> Writer<W> {
    fn new(wtr: W, color: bool) -> Writer<W> {
        // If we want color, build a TerminfoTerminal and see if the current
        // environment supports coloring. If not, bail with NoColor. To avoid
        // losing our writer (ownership), do this the long way.
        if !color || TERMINFO.is_none() {
            return NoColor(wtr);
        }
        let info = TERMINFO.clone().unwrap();
        let tt = TerminfoTerminal::new_with_terminfo(wtr, info);
        if !tt.supports_color() {
            debug!("environment doesn't support coloring");
            return NoColor(tt.into_inner());
        }
        Colored(tt)
    }

    fn is_color(&self) -> bool {
        match *self {
            Colored(_) => true,
            NoColor(_) => false,
        }
    }

    fn map_result<F>(
        &mut self,
        mut f: F,
    ) -> term::Result<()>
    where F: FnMut(&mut TerminfoTerminal<W>) -> term::Result<()> {
        match *self {
            Colored(ref mut w) => f(w),
            NoColor(_) => Err(term::Error::NotSupported),
        }
    }

    fn map_bool<F>(
        &self,
        mut f: F,
    ) -> bool
    where F: FnMut(&TerminfoTerminal<W>) -> bool {
        match *self {
            Colored(ref w) => f(w),
            NoColor(_) => false,
        }
    }
}

impl<W: Send + io::Write> io::Write for Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Colored(ref mut w) => w.write(buf),
            NoColor(ref mut w) => w.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Colored(ref mut w) => w.flush(),
            NoColor(ref mut w) => w.flush(),
        }
    }
}

impl<W: Send + io::Write> term::Terminal for Writer<W> {
    type Output = W;

    fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.fg(fg))
    }

    fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.bg(bg))
    }

    fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
        self.map_result(|w| w.attr(attr))
    }

    fn supports_attr(&self, attr: term::Attr) -> bool {
        self.map_bool(|w| w.supports_attr(attr))
    }

    fn reset(&mut self) -> term::Result<()> {
        self.map_result(|w| w.reset())
    }

    fn supports_reset(&self) -> bool {
        self.map_bool(|w| w.supports_reset())
    }

    fn supports_color(&self) -> bool {
        self.map_bool(|w| w.supports_color())
    }

    fn cursor_up(&mut self) -> term::Result<()> {
        self.map_result(|w| w.cursor_up())
    }

    fn delete_line(&mut self) -> term::Result<()> {
        self.map_result(|w| w.delete_line())
    }

    fn carriage_return(&mut self) -> term::Result<()> {
        self.map_result(|w| w.carriage_return())
    }

    fn get_ref(&self) -> &W {
        match *self {
            Colored(ref w) => w.get_ref(),
            NoColor(ref w) => w,
        }
    }

    fn get_mut(&mut self) -> &mut W {
        match *self {
            Colored(ref mut w) => w.get_mut(),
            NoColor(ref mut w) => w,
        }
    }

    fn into_inner(self) -> W {
        match self {
            Colored(w) => w.into_inner(),
            NoColor(w) => w,
        }
    }
}
<commit_msg>Tweak colors.<commit_after>use std::io::{self, Write};
use std::path::Path;
use std::sync::Arc;

use regex::bytes::Regex;
use term::{self, Terminal};
use term::color::*;
use term::terminfo::TermInfo;

use terminal::TerminfoTerminal;
use types::FileTypeDef;

use self::Writer::*;

/// Printer encapsulates all output logic for searching.
///
/// Note that we currently ignore all write errors. It's probably worthwhile
/// to fix this, but printers are only ever used for writes to stdout or
/// writes to memory, neither of which commonly fail.
pub struct Printer<W> {
    /// The underlying writer.
    wtr: Writer<W>,
    /// Whether anything has been printed to wtr yet.
    has_printed: bool,
    /// The string to use to separate non-contiguous runs of context lines.
    context_separator: Vec<u8>,
    /// The end-of-line terminator used by the printer. In general, eols are
    /// printed via the match directly, but occasionally we need to insert them
    /// ourselves (for example, to print a context separator).
    eol: u8,
    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    heading: bool,
    /// Whether to suppress all output.
    quiet: bool,
    /// A string to use as a replacement of each match in a matching line.
    replace: Option<Vec<u8>>,
    /// Whether to prefix each match with the corresponding file name.
    with_filename: bool,
}

impl<W: Send + io::Write> Printer<W> {
    /// Create a new printer that writes to wtr.
    ///
    /// `color` should be true if the printer should try to use coloring.
    pub fn new(wtr: W, color: bool) -> Printer<W> {
        Printer {
            wtr: Writer::new(wtr, color),
            has_printed: false,
            context_separator: "--".to_string().into_bytes(),
            eol: b'\n',
            heading: false,
            quiet: false,
            replace: None,
            with_filename: false,
        }
    }

    /// Set the context separator. The default is `--`.
    pub fn context_separator(mut self, sep: Vec<u8>) -> Printer<W> {
        self.context_separator = sep;
        self
    }

    /// Set the end-of-line terminator. The default is `\n`.
    pub fn eol(mut self, eol: u8) -> Printer<W> {
        self.eol = eol;
        self
    }

    /// Whether to show file name as a heading or not.
    ///
    /// N.B. If with_filename is false, then this setting has no effect.
    pub fn heading(mut self, yes: bool) -> Printer<W> {
        self.heading = yes;
        self
    }

    /// When set, all output is suppressed.
    pub fn quiet(mut self, yes: bool) -> Printer<W> {
        self.quiet = yes;
        self
    }

    /// Replace every match in each matching line with the replacement string
    /// given.
    ///
    /// The replacement string syntax is documented here:
    /// https://doc.rust-lang.org/regex/regex/bytes/struct.Captures.html#method.expand
    pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> {
        self.replace = Some(replacement);
        self
    }

    /// When set, each match is prefixed with the file name that it came from.
    pub fn with_filename(mut self, yes: bool) -> Printer<W> {
        self.with_filename = yes;
        self
    }

    /// Returns true if and only if something has been printed.
    pub fn has_printed(&self) -> bool {
        self.has_printed
    }

    /// Flushes the underlying writer and returns it.
    pub fn into_inner(mut self) -> W {
        let _ = self.wtr.flush();
        self.wtr.into_inner()
    }

    /// Prints a type definition.
    pub fn type_def(&mut self, def: &FileTypeDef) {
        self.write(def.name().as_bytes());
        self.write(b": ");
        let mut first = true;
        for pat in def.patterns() {
            if !first {
                self.write(b", ");
            }
            self.write(pat.as_bytes());
            first = false;
        }
        self.write_eol();
    }

    /// Prints the given path.
    pub fn path<P: AsRef<Path>>(&mut self, path: P) {
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
    }

    /// Prints the given path and a count of the number of matches found.
    pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
        if self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        self.write(count.to_string().as_bytes());
        self.write_eol();
    }

    /// Prints the context separator.
    pub fn context_separate(&mut self) {
        // N.B. We can't use `write` here because of borrowing restrictions.
        if self.quiet {
            return;
        }
        if self.context_separator.is_empty() {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(&self.context_separator);
        let _ = self.wtr.write_all(&[self.eol]);
    }

    pub fn matched<P: AsRef<Path>>(
        &mut self,
        re: &Regex,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b":");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b':');
        }
        if self.replace.is_some() {
            let line = re.replace_all(
                &buf[start..end], &**self.replace.as_ref().unwrap());
            self.write(&line);
        } else {
            self.write_match(re, &buf[start..end]);
        }
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    pub fn write_match(&mut self, re: &Regex, buf: &[u8]) {
        if !self.wtr.is_color() {
            self.write(buf);
            return;
        }
        let mut last_written = 0;
        for (s, e) in re.find_iter(buf) {
            self.write(&buf[last_written..s]);
            let _ = self.wtr.fg(RED);
            let _ = self.wtr.attr(term::Attr::Bold);
            self.write(&buf[s..e]);
            let _ = self.wtr.reset();
            last_written = e;
        }
        self.write(&buf[last_written..]);
    }

    pub fn context<P: AsRef<Path>>(
        &mut self,
        path: P,
        buf: &[u8],
        start: usize,
        end: usize,
        line_number: Option<u64>,
    ) {
        if self.heading && self.with_filename && !self.has_printed {
            self.write_heading(path.as_ref());
        } else if !self.heading && self.with_filename {
            self.write(path.as_ref().to_string_lossy().as_bytes());
            self.write(b"-");
        }
        if let Some(line_number) = line_number {
            self.line_number(line_number, b'-');
        }
        self.write(&buf[start..end]);
        if buf[start..end].last() != Some(&self.eol) {
            self.write_eol();
        }
    }

    fn write_heading<P: AsRef<Path>>(&mut self, path: P) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(GREEN);
            let _ = self.wtr.attr(term::Attr::Bold);
        }
        self.write(path.as_ref().to_string_lossy().as_bytes());
        self.write_eol();
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
    }

    fn line_number(&mut self, n: u64, sep: u8) {
        if self.wtr.is_color() {
            let _ = self.wtr.fg(BLUE);
            let _ = self.wtr.attr(term::Attr::Bold);
        }
        self.write(n.to_string().as_bytes());
        if self.wtr.is_color() {
            let _ = self.wtr.reset();
        }
        self.write(&[sep]);
    }

    fn write(&mut self, buf: &[u8]) {
        if self.quiet {
            return;
        }
        self.has_printed = true;
        let _ = self.wtr.write_all(buf);
    }

    fn write_eol(&mut self) {
        let eol = self.eol;
        self.write(&[eol]);
    }
}

enum Writer<W> {
    Colored(TerminfoTerminal<W>),
    NoColor(W),
}

lazy_static! {
    static ref TERMINFO: Option<Arc<TermInfo>> = {
        match term::terminfo::TermInfo::from_env() {
            Ok(info) => Some(Arc::new(info)),
            Err(err) => {
                debug!("error loading terminfo for coloring: {}", err);
                None
            }
        }
    };
}

impl<W: Send + io::Write> Writer<W> {
    fn new(wtr: W, color: bool) -> Writer<W> {
        // If we want color, build a TerminfoTerminal and see if the current
        // environment supports coloring. If not, bail with NoColor. To avoid
        // losing our writer (ownership), do this the long way.
        if !color || TERMINFO.is_none() {
            return NoColor(wtr);
        }
        let info = TERMINFO.clone().unwrap();
        let tt = TerminfoTerminal::new_with_terminfo(wtr, info);
        if !tt.supports_color() {
            debug!("environment doesn't support coloring");
            return NoColor(tt.into_inner());
        }
        Colored(tt)
    }

    fn is_color(&self) -> bool {
        match *self {
            Colored(_) => true,
            NoColor(_) => false,
        }
    }

    fn map_result<F>(
        &mut self,
        mut f: F,
    ) -> term::Result<()>
    where F: FnMut(&mut TerminfoTerminal<W>) -> term::Result<()> {
        match *self {
            Colored(ref mut w) => f(w),
            NoColor(_) => Err(term::Error::NotSupported),
        }
    }

    fn map_bool<F>(
        &self,
        mut f: F,
    ) -> bool
    where F: FnMut(&TerminfoTerminal<W>) -> bool {
        match *self {
            Colored(ref w) => f(w),
            NoColor(_) => false,
        }
    }
}

impl<W: Send + io::Write> io::Write for Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Colored(ref mut w) => w.write(buf),
            NoColor(ref mut w) => w.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Colored(ref mut w) => w.flush(),
            NoColor(ref mut w) => w.flush(),
        }
    }
}

impl<W: Send + io::Write> term::Terminal for Writer<W> {
    type Output = W;

    fn fg(&mut self, fg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.fg(fg))
    }

    fn bg(&mut self, bg: term::color::Color) -> term::Result<()> {
        self.map_result(|w| w.bg(bg))
    }

    fn attr(&mut self, attr: term::Attr) -> term::Result<()> {
        self.map_result(|w| w.attr(attr))
    }

    fn supports_attr(&self, attr: term::Attr) -> bool {
        self.map_bool(|w| w.supports_attr(attr))
    }

    fn reset(&mut self) -> term::Result<()> {
        self.map_result(|w| w.reset())
    }

    fn supports_reset(&self) -> bool {
        self.map_bool(|w| w.supports_reset())
    }

    fn supports_color(&self) -> bool {
        self.map_bool(|w| w.supports_color())
    }

    fn cursor_up(&mut self) -> term::Result<()> {
        self.map_result(|w| w.cursor_up())
    }

    fn delete_line(&mut self) -> term::Result<()> {
        self.map_result(|w| w.delete_line())
    }

    fn carriage_return(&mut self) -> term::Result<()> {
        self.map_result(|w| w.carriage_return())
    }

    fn get_ref(&self) -> &W {
        match *self {
            Colored(ref w) => w.get_ref(),
            NoColor(ref w) => w,
        }
    }

    fn get_mut(&mut self) -> &mut W {
        match *self {
            Colored(ref mut w) => w.get_mut(),
            NoColor(ref mut w) => w,
        }
    }

    fn into_inner(self) -> W {
        match self {
            Colored(w) => w.into_inner(),
            NoColor(w) => w,
        }
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

pub use self::imp::*;

#[cfg(target_family = "unix")]
mod imp {
    use std::io::Result;
    use std::path::PathBuf;
    use std::process::Command;

    pub struct Process {
        pid: i32,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::io;
            use std::os::unix::process::CommandExt;
            use libc::exit;
            use nix::unistd::*;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            // Until process_exec lands in stable, handle fork/exec ourselves
            //command.before_exec(|| setpgid(0, 0).map_err(io::Error::from))
                //.spawn()
                //.and_then(|p| Ok(Process { pid: p.id() as i32 }))

            match fork() {
                Ok(ForkResult::Parent {child, .. }) => {
                    Ok(Process {
                        pid: child as i32
                    })
                },
                Ok(ForkResult::Child) => {
                    let _ = setpgid(0, 0);
                    let _ = command.exec();

                    // If we get here, there isn't much we can do
                    unsafe {
                        exit(1);
                    }
                }
                Err(e) => { Err(io::Error::from(e)) }
            }
        }

        pub fn kill(&self) {
            use libc;

            extern "C" {
                fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
            }

            unsafe {
                killpg(self.pid, libc::SIGTERM);
            }
        }

        pub fn wait(&self) {
            use nix::sys::wait::waitpid;

            let _ = waitpid(-self.pid, None);
        }
    }
}

#[cfg(target_family = "windows")]
mod imp {
    use std::io;
    use std::io::Result;
    use std::mem;
    use std::path::PathBuf;
    use std::process::Command;
    use kernel32::*;
    use winapi::*;

    pub struct Process {
        job: HANDLE,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::os::windows::io::IntoRawHandle;

            fn last_err() -> io::Error {
                io::Error::last_os_error()
            }

            let job = unsafe { CreateJobObjectW(0 as *mut _, 0 as *const _) };
            if job.is_null() {
                panic!("failed to create job object: {}", last_err());
            }

            let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() };
            info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
            let r = unsafe {
                SetInformationJobObject(job,
                                        JobObjectExtendedLimitInformation,
                                        &mut info as *mut _ as LPVOID,
                                        mem::size_of_val(&info) as DWORD)
            };
            if r == 0 {
                panic!("failed to set job info: {}", last_err());
            }

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            command.spawn()
                .and_then(|p| {
                    let r = unsafe { AssignProcessToJobObject(job, p.into_raw_handle()) };
                    if r == 0 {
                        panic!("failed to add to job object: {}", last_err());
                    }

                    Ok(Process { job: job })
                })
        }

        pub fn kill(&self) {
            unsafe {
                let _ = TerminateJobObject(self.job, 1);
            }
        }

        pub fn wait(&self) {
            unsafe {
                let _ = WaitForSingleObject(self.job, INFINITE);
            }
        }
    }

    impl Drop for Process {
        fn drop(&mut self) {
            unsafe {
                let _ = CloseHandle(self.job);
            }
        }
    }

    unsafe impl Send for Process {}
    unsafe impl Sync for Process {}
}

fn get_single_updated_path(paths: &[PathBuf]) -> Option<&str> {
    paths.get(0).and_then(|p| p.to_str())
}

fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
    match paths.len() {
        0 => return None,
        1 => return paths[0].to_str().map(|ref_val| ref_val.to_string()),
        _ => {}
    };

    let mut longest_path: Vec<_> = paths[0].components().collect();

    for path in &paths[1..] {
        let mut greatest_distance = 0;
        for component_pair in path.components().zip(longest_path.iter()) {
            if component_pair.0 != *component_pair.1 {
                break;
            }

            greatest_distance += 1;
        }

        if greatest_distance != longest_path.len() {
            longest_path.truncate(greatest_distance);
        }
    }

    let mut result = PathBuf::new();
    for component in longest_path {
        result.push(component.as_os_str());
    }

    result.to_str().map(|ref_val| ref_val.to_string())
}

#[cfg(test)]
#[cfg(target_family = "unix")]
mod tests {
    use std::path::{Path, PathBuf};
    use std::thread;
    use std::time::Duration;

    use mktemp::Temp;

    use super::imp::Process;
    use super::get_longest_common_path;

    fn file_contents(path: &Path) -> String {
        use std::fs::File;
        use std::io::Read;

        let mut f = File::open(path).unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        s
    }

    #[test]
    fn test_start() {
        let process = Process::new("echo hi", vec![]);

        assert!(process.is_ok());
    }

    #[test]
    fn test_wait() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();
        let process = Process::new(&format!("echo hi > {}", path.to_str().unwrap()), vec![])
            .unwrap();
        process.wait();

        assert!(file_contents(&path).starts_with("hi"));
    }

    #[test]
    fn test_kill() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();

        let process = Process::new(&format!("sleep 20; echo hi > {}", path.to_str().unwrap()),
                                       vec![])
            .unwrap();
        thread::sleep(Duration::from_millis(250));
        process.kill();
        process.wait();

        assert!(file_contents(&path) == "");
    }

    #[test]
    fn longest_common_path_should_return_correct_value() {
        let single_path = vec![PathBuf::from("/tmp/random/")];
        let single_result = get_longest_common_path(&single_path).unwrap();
        assert_eq!(single_result, "/tmp/random/");

        let common_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/fly")];

        let common_result = get_longest_common_path(&common_paths).unwrap();
        assert_eq!(common_result, "/tmp/logs");

        let diverging_paths = vec![PathBuf::from("/tmp/logs/hi"), PathBuf::from("/var/logs/hi")];

        let diverging_result = get_longest_common_path(&diverging_paths).unwrap();
        assert_eq!(diverging_result, "/");

        let uneven_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/"),
                                PathBuf::from("/tmp/logs/bye")];

        let uneven_result = get_longest_common_path(&uneven_paths).unwrap();
        assert_eq!(uneven_result, "/tmp/logs");
    }
}
<commit_msg>Wait for child process, not any child process in group<commit_after>use std::path::PathBuf;

pub use self::imp::*;

#[cfg(target_family = "unix")]
mod imp {
    use std::io::Result;
    use std::path::PathBuf;
    use std::process::Command;

    pub struct Process {
        pid: i32,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::io;
            use std::os::unix::process::CommandExt;
            use libc::exit;
            use nix::unistd::*;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            // Until process_exec lands in stable, handle fork/exec ourselves
            //command.before_exec(|| setpgid(0, 0).map_err(io::Error::from))
                //.spawn()
                //.and_then(|p| Ok(Process { pid: p.id() as i32 }))

            match fork() {
                Ok(ForkResult::Parent {child, .. }) => {
                    Ok(Process {
                        pid: child as i32
                    })
                },
                Ok(ForkResult::Child) => {
                    let _ = setpgid(0, 0);
                    let _ = command.exec();

                    // If we get here, there isn't much we can do
                    unsafe {
                        exit(1);
                    }
                }
                Err(e) => { Err(io::Error::from(e)) }
            }
        }

        pub fn kill(&self) {
            use libc;

            extern "C" {
                fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
            }

            unsafe {
                killpg(self.pid, libc::SIGTERM);
            }
        }

        pub fn wait(&self) {
            use nix::sys::wait::waitpid;

            let _ = waitpid(self.pid, None);
        }
    }
}

#[cfg(target_family = "windows")]
mod imp {
    use std::io;
    use std::io::Result;
    use std::mem;
    use std::path::PathBuf;
    use std::process::Command;
    use kernel32::*;
    use winapi::*;

    pub struct Process {
        job: HANDLE,
    }

    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<PathBuf>) -> Result<Process> {
            use std::os::windows::io::IntoRawHandle;

            fn last_err() -> io::Error {
                io::Error::last_os_error()
            }

            let job = unsafe { CreateJobObjectW(0 as *mut _, 0 as *const _) };
            if job.is_null() {
                panic!("failed to create job object: {}", last_err());
            }

            let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() };
            info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
            let r = unsafe {
                SetInformationJobObject(job,
                                        JobObjectExtendedLimitInformation,
                                        &mut info as *mut _ as LPVOID,
                                        mem::size_of_val(&info) as DWORD)
            };
            if r == 0 {
                panic!("failed to set job info: {}", last_err());
            }

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if let Some(single_path) = super::get_single_updated_path(&updated_paths) {
                command.env("WATCHEXEC_UPDATED_PATH", single_path);
            }

            if let Some(common_path) = super::get_longest_common_path(&updated_paths) {
                command.env("WATCHEXEC_COMMON_PATH", common_path);
            }

            command.spawn()
                .and_then(|p| {
                    let r = unsafe { AssignProcessToJobObject(job, p.into_raw_handle()) };
                    if r == 0 {
                        panic!("failed to add to job object: {}", last_err());
                    }

                    Ok(Process { job: job })
                })
        }

        pub fn kill(&self) {
            unsafe {
                let _ = TerminateJobObject(self.job, 1);
            }
        }

        pub fn wait(&self) {
            unsafe {
                let _ = WaitForSingleObject(self.job, INFINITE);
            }
        }
    }

    impl Drop for Process {
        fn drop(&mut self) {
            unsafe {
                let _ = CloseHandle(self.job);
            }
        }
    }

    unsafe impl Send for Process {}
    unsafe impl Sync for Process {}
}

fn get_single_updated_path(paths: &[PathBuf]) -> Option<&str> {
    paths.get(0).and_then(|p| p.to_str())
}

fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
    match paths.len() {
        0 => return None,
        1 => return paths[0].to_str().map(|ref_val| ref_val.to_string()),
        _ => {}
    };

    let mut longest_path: Vec<_> = paths[0].components().collect();

    for path in &paths[1..] {
        let mut greatest_distance = 0;
        for component_pair in path.components().zip(longest_path.iter()) {
            if component_pair.0 != *component_pair.1 {
                break;
            }

            greatest_distance += 1;
        }

        if greatest_distance != longest_path.len() {
            longest_path.truncate(greatest_distance);
        }
    }

    let mut result = PathBuf::new();
    for component in longest_path {
        result.push(component.as_os_str());
    }

    result.to_str().map(|ref_val| ref_val.to_string())
}

#[cfg(test)]
#[cfg(target_family = "unix")]
mod tests {
    use std::path::{Path, PathBuf};
    use std::thread;
    use std::time::Duration;

    use mktemp::Temp;

    use super::imp::Process;
    use super::get_longest_common_path;

    fn file_contents(path: &Path) -> String {
        use std::fs::File;
        use std::io::Read;

        let mut f = File::open(path).unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        s
    }

    #[test]
    fn test_start() {
        let process = Process::new("echo hi", vec![]);

        assert!(process.is_ok());
    }

    #[test]
    fn test_wait() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();
        let process = Process::new(&format!("echo hi > {}", path.to_str().unwrap()), vec![])
            .unwrap();
        process.wait();

        assert!(file_contents(&path).starts_with("hi"));
    }

    #[test]
    fn test_kill() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();

        let process = Process::new(&format!("sleep 20; echo hi > {}", path.to_str().unwrap()),
                                       vec![])
            .unwrap();
        thread::sleep(Duration::from_millis(250));
        process.kill();
        process.wait();

        assert!(file_contents(&path) == "");
    }

    #[test]
    fn longest_common_path_should_return_correct_value() {
        let single_path = vec![PathBuf::from("/tmp/random/")];
        let single_result = get_longest_common_path(&single_path).unwrap();
        assert_eq!(single_result, "/tmp/random/");

        let common_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/bye"),
                                PathBuf::from("/tmp/logs/fly")];

        let common_result = get_longest_common_path(&common_paths).unwrap();
        assert_eq!(common_result, "/tmp/logs");

        let diverging_paths = vec![PathBuf::from("/tmp/logs/hi"), PathBuf::from("/var/logs/hi")];

        let diverging_result = get_longest_common_path(&diverging_paths).unwrap();
        assert_eq!(diverging_result, "/");

        let uneven_paths = vec![PathBuf::from("/tmp/logs/hi"),
                                PathBuf::from("/tmp/logs/"),
                                PathBuf::from("/tmp/logs/bye")];

        let uneven_result = get_longest_common_path(&uneven_paths).unwrap();
        assert_eq!(uneven_result, "/tmp/logs");
    }
}
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    pub is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                if self.is_seeking {
                    let (new_index, still_seeking) = self.seek_forward(index);
                    self.is_seeking = still_seeking;
                    index = new_index;
                }
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.start_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize) -> (usize, bool) {
        let goal_depth = self.goal_depth.unwrap();
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            if self.current_depth == goal_depth { return (index + 1, false) }
        }
        (self.instructions.len(), true)
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            let (index, still_seeking) = self.seek_forward(index);
            self.is_seeking = still_seeking;
            return index;
        } else {
            self.current_depth = self.current_depth + 1;
            return index;
        }
    }
}
<commit_msg>Prevent infinite loops on a loop situation<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    pub is_seeking: bool,
    current_depth: u64,
    goal_depth: Option<u64>,
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            is_seeking: false,
            current_depth: 0,
            goal_depth: None,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match self.instruction_pointer {
            None => {},
            Some(mut index) => {
                if self.is_seeking {
                    let (new_index, still_seeking) = self.seek_forward(index);
                    self.is_seeking = still_seeking;
                    index = new_index;
                }
                while index < self.instructions.len() {
                    let command = self.instructions[index];
                    if command == Command::JumpForward {
                        index = self.start_jump_forward(vm, index);
                    } else if command == Command::JumpBackward {
                        self.current_depth = self.current_depth - 1
                    } else {
                        vm.apply(command);
                        index = index + 1;
                    }
                }
                self.instruction_pointer = Some(index);
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize) -> (usize, bool) {
        let goal_depth = self.goal_depth.unwrap();
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth = self.current_depth + 1,
                Command::JumpBackward => self.current_depth = self.current_depth - 1,
                _ => {},
            }
            if self.current_depth == goal_depth { return (index + 1, false) }
        }
        (self.instructions.len(), true)
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> usize {
        if vm.cells[vm.data_pointer] == 0 {
            self.goal_depth = Some(self.current_depth);
            let (index, still_seeking) = self.seek_forward(index);
            self.is_seeking = still_seeking;
            return index;
        } else {
            self.current_depth = self.current_depth + 1;
            return index + 1;
        }
    }
}
<|endoftext|>


<commit_before>//! The module that contains the request code.

use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::io::Error;
use std::str::from_utf8;
use std::sync::mpsc::channel;
use std::time::Duration;
use super::Method;
use curl::easy::{Easy, List};
use futures::{BoxFuture, failed, Future};
use response::Response;
use tokio_core::reactor::Handle;
use tokio_curl::Session;
use url::Url;

#[cfg(feature = "rustc-serialization")]
use rustc_serialize;

#[cfg(feature = "serde-serialization")]
use serde;
#[cfg(feature = "serde-serialization")]
use serde_json;

/// The default low byte rate threshold.
///
/// See [`Request::lowspeed_limit`](struct.Request.html#method.lowspeed_limit)
/// for more information.
pub const LOW_SPEED_LIMIT: u32 = 10;

/// The default low speed time threshold in seconds.
///
/// See [`Request::lowspeed_limit`](struct.Request.html#method.lowspeed_limit)
/// for more information.
pub const LOW_SPEED_TIME: u32 = 10;

/// The default redirect threshold for a single request.
///
/// cURL will follow this many redirects by default before aborting
/// the request. See [`Request::max_redirects`](struct.Request.html#method.max_redirects)
/// for more information.
pub const MAX_REDIRECTS: u32 = 15;

/// Represents an HTTP request.
///
/// While this can be used directly (and _must_ be for special HTTP verbs, it is
/// preferred to use the [`get`](fn.get.html), [`post`](fn.post.html), etc. functions
/// since they are shorter.
pub struct Request {
    body: Option<Vec<u8>>,
    follow_redirects: bool,
    handle: Option<Easy>,
    headers: Vec<(String, String)>,
    lowspeed_limits: Option<(u32, Duration)>,
    max_redirects: u32,
    method: Method,
    params: Vec<(String, String)>,
    timeout: Option<Duration>,
    url: Url
}

impl Request {
    /// Creates a new instance of `Request`.
    pub fn new(url: &Url, method: Method) -> Self {
        Request {
            body: None,
            follow_redirects: true,
            handle: None,
            headers: Vec::new(),
            lowspeed_limits: Some((LOW_SPEED_LIMIT, Duration::from_secs(LOW_SPEED_TIME as u64))),
            max_redirects: MAX_REDIRECTS,
            method: method,
            params: Vec::new(),
            timeout: None,
            url: url.clone()
        }
    }

    /// Sets the body of the request as raw byte array.
    pub fn body<B: Into<Vec<u8>>>(mut self, body: B) -> Self {
        self.body = Some(body.into());
        self
    }

    /// Sets the option whether to follow 3xx-redirects or not.
    ///
    /// Defaults to `true`.
    pub fn follow_redirects(mut self, follow: bool) -> Self {
        self.follow_redirects = follow;
        self
    }

    /// Adds an HTTP header to the request.
    pub fn header(mut self, name: &str, value: &str) -> Self {
        self.headers.push((name.to_owned(), value.to_owned()));
        self
    }

    /// Sets the given request headers.
    ///
    /// This overwrites all previously set headers.
    pub fn headers(mut self, headers: Vec<(String, String)>) -> Self {
        self.headers = headers;
        self
    }

    /// Serializes the given object to JSON and uses that as the request body.
    /// Also automatically sets the `Content-Type` to `application/json`.
    ///
    /// ## Panics
    /// Panics if serialization is not successful.
    #[cfg(feature = "rustc-serialization")]
    pub fn json<T: rustc_serialize::Encodable>(self, body: &T) -> Self {
        self.set_json(rustc_serialize::json::encode(body).unwrap().into_bytes())
    }

    /// Serializes the given object to JSON and uses that as the request body.
    /// Also automatically sets the `Content-Type` to `application/json`.
    ///
    /// ## Panics
    /// Panics if serialization is not successful.
    #[cfg(feature = "serde-serialization")]
    pub fn json<T: serde::Serialize>(self, body: &T) -> Self {
        self.set_json(serde_json::to_vec(body).unwrap())
    }

    /// Sets the thresholds which, when reached, aborts a download due to too
    /// low speeds.
    ///
    /// Pass 0 for either parameter to disable lowspeed limiting.
    ///
    /// ## Remarks
    /// `bytes` sets the minimum average amount of bytes transferred in `per_duration`
    /// time. If this number is not reached, cURL will abort the transfer because the transfer
    /// speed is too low.
    ///
    /// The values here default to [`LOW_SPEED_LIMIT`](constant.LOW_SPEED_LIMIT.html) and
    /// [`LOW_SPEED_TIME`](constant.LOW_SPEED_TIME.html).
    pub fn lowspeed_limit(mut self, bytes: u32, per_duration: Duration) -> Self {
        self.lowspeed_limits = if bytes > 0 && per_duration > Duration::from_secs(0) {
            Some((bytes, per_duration))
        } else {
            None
        };
        self
    }

    /// Sets the maximum amount of redirects cURL will follow when
    /// [`Request::follow_redirects`](#method.follow_redirects) is
    /// enabled.
    pub fn max_redirects(mut self, max_redirects: u32) -> Self {
        self.max_redirects = max_redirects;
        self
    }

    /// Adds a URL parameter to the request.
    pub fn param(mut self, name: &str, value: &str) -> Self {
        self.params.push((name.to_owned(), value.to_owned()));
        self
    }

    /// Creates a new `Session` on the specified event loop to send the HTTP request through
    /// and returns a future that fires off the request, parses the response and resolves to
    /// a `Response`-struct on success.
    ///
    /// ## Panics
    /// Panics in case of native exceptions in cURL.
    pub fn send(self, h: Handle) -> BoxFuture<Response, Error> {
        self.send_with_session(&Session::new(h))
    }

    /// Uses the given `Session` to send the HTTP request through and returns a future that
    /// fires off the request, parses the response and resolves to a `Response`-struct on success.
    ///
    /// ## Panics
    /// Panics in case of native exceptions in cURL.
    pub fn send_with_session(mut self, session: &Session) -> BoxFuture<Response, Error> {
        {
            let mut query_pairs = self.url.query_pairs_mut();
            for (key, value) in self.params {
                query_pairs.append_pair(key.trim(), value.trim());
            }
        }
        let headers = {
            let mut list = List::new();
            for (key, value) in self.headers {
                list.append(&format!("{}: {}", key.trim(), value.trim())).expect("Failed to append header value to (native cURL) header list.");
            }
            list
        };

        let mut easy = self.handle.unwrap_or_else(|| Easy::new());
        let (header_tx, header_rx) = channel();
        let (body_tx, body_rx) = channel();

        let config_res = {
            // Make the borrow checker happy
            let body = self.body;
            let follow_redirects = self.follow_redirects;
            let lowspeed_limits = self.lowspeed_limits;
            let max_redirects = self.max_redirects;
            let method = self.method;
            let timeout = self.timeout;
            let url = self.url;
            let mut first_header = true;

            // We cannot use try! here, since we're dealing with futures, not with Results
            Ok(())
                .and_then(|_| easy.accept_encoding(""))
                .and_then(|_| easy.custom_request(method.as_ref()))
                .and_then(|_| if follow_redirects {
                    easy.follow_location(true)
                        .and_then(|_| easy.max_redirections(max_redirects))
                } else {
                    Ok(())
                })
                .and_then(|_| easy.header_function(move |header| {
                    match from_utf8(header) {
                        Ok(s) => {
                            let s = s.trim(); // Headers are \n-separated
                            if !first_header && s.len() > 0 { // First header is HTTP status line, don't want that
                                let _ = header_tx.send(s.to_owned());
                            }
                            first_header = false;
                            true
                        },
                        Err(_) => false
                    }
                }))
                .and_then(|_| easy.http_headers(headers))
                .and_then(|_| if let Some((bytes, per_time)) = lowspeed_limits {
                    easy.low_speed_limit(bytes)
                        .and_then(|_| easy.low_speed_time(per_time))
                } else {
                    Ok(())
                })
                .and_then(|_| if method == Method::Head {
                    easy.nobody(true)
                } else {
                    Ok(())
                })
                .and_then(|_| if let Some(ref body) = body {
                    easy.post_fields_copy(body)
                } else {
                    Ok(())
                })
                .and_then(|_| if let Some(timeout) = timeout {
                    easy.timeout(timeout)
                } else {
                    Ok(())
                })
                .and_then(|_| easy.url(url.as_str()))
                .and_then(|_| easy.write_function(move |data| {
                    let _ = body_tx.send(Vec::from(data));
                    Ok(data.len())
                }))
        };

        match config_res {
            Ok(_) => session.perform(easy)
                            .map_err(|err| err.into_error())
                            .map(move |ez| {
                                let body = body_rx.try_iter().fold(Vec::new(), |mut data, slice| {
                                    data.extend(slice);
                                    data
                                });
                                let headers = header_rx.try_iter().collect::<Vec<_>>();
                                Response::new(ez, headers, body)
                            })
                            .boxed(),
            Err(error) => failed(error.into()).boxed()
        }
    }

    /// Set the maximum time the request is allowed to take.
    ///
    /// Disabled by default in favor of [`lowspeed_limit`]
    pub fn timeout(mut self, duration: Duration) -> Self {
        self.timeout = Some(duration);
        self
    }

    /// Uses the given cURL handle in the request process reusing its resources
    /// and improving performance.
    ///
    /// This is solely a way to improve performance, it is not necessary to call
    /// this method prior to firing off the request. The easy handle will be created
    /// automatically if necessary.
    pub fn use_handle(mut self, handle: Easy) -> Self {
        self.handle = Some(handle);
        self
    }

    #[cfg(any(feature = "rustc-serialization", feature = "serde-serialization"))]
    fn set_json(mut self, body: Vec<u8>) -> Self {
        self.body = Some(body);
        self.header("Content-Type", "application/json")
    }
}

impl Debug for Request {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        let len = if let Some(ref body) = self.body {
            body.len() as isize
        } else {
            -1isize
        };
        fmt.debug_struct(stringify!(Request))
            .field("body_len", &len)
            .field("follow_redirects", &self.follow_redirects)
            .field("headers", &self.headers)
            .field("method", &self.method)
            .field("params", &self.params)
            .field("reuses_handle", &self.handle.is_some())
            .field("url", &self.url)
            .finish()
    }
}

impl Display for Request {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        write!(fmt, "{} {}", self.method, self.url)
    }
}

#[cfg(test)]
mod tests {
    use ::{Method, Request};
    use url::Url;

    #[cfg(feature = "rustc-serialization")]
    use rustc_serialize;

    #[cfg(feature = "serde-serialization")]
    use serde_json;

    #[cfg_attr(feature = "rustc-serialization", derive(RustcEncodable, RustcDecodable))]
    #[cfg_attr(feature = "serde-serialization", derive(Serialize, Deserialize))]
    struct TestPayload {
        a: u32,
        b: u32
    }

    #[test]
    #[cfg(any(feature = "rustc-serialization", feature = "serde-serialization"))]
    fn test_payload() {
        let r = Request::new(&Url::parse("http://google.com/").unwrap(), Method::Get)
            .body(get_serialized_payload());
        assert!(r.body.is_some());
    }

    #[cfg(feature = "rustc-serialization")]
    fn get_serialized_payload() -> Vec<u8> {
        rustc_serialize::json::encode(&TestPayload { a: 10, b: 15 }).unwrap().into_bytes()
    }

    #[cfg(feature = "serde-serialization")]
    fn get_serialized_payload() -> Vec<u8> {
        serde_json::to_vec(&TestPayload { a: 10, b: 15}).unwrap()
    }
}<commit_msg>Add method to add multiple parameters to Request<commit_after>//! The module that contains the request code.

use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::io::Error;
use std::str::from_utf8;
use std::sync::mpsc::channel;
use std::time::Duration;
use super::Method;
use curl::easy::{Easy, List};
use futures::{BoxFuture, failed, Future};
use response::Response;
use tokio_core::reactor::Handle;
use tokio_curl::Session;
use url::Url;

#[cfg(feature = "rustc-serialization")]
use rustc_serialize;

#[cfg(feature = "serde-serialization")]
use serde;
#[cfg(feature = "serde-serialization")]
use serde_json;

/// The default low byte rate threshold.
///
/// See [`Request::lowspeed_limit`](struct.Request.html#method.lowspeed_limit)
/// for more information.
pub const LOW_SPEED_LIMIT: u32 = 10;

/// The default low speed time threshold in seconds.
///
/// See [`Request::lowspeed_limit`](struct.Request.html#method.lowspeed_limit)
/// for more information.
pub const LOW_SPEED_TIME: u32 = 10;

/// The default redirect threshold for a single request.
///
/// cURL will follow this many redirects by default before aborting
/// the request. See [`Request::max_redirects`](struct.Request.html#method.max_redirects)
/// for more information.
pub const MAX_REDIRECTS: u32 = 15;

/// Represents an HTTP request.
///
/// While this can be used directly (and _must_ be for special HTTP verbs, it is
/// preferred to use the [`get`](fn.get.html), [`post`](fn.post.html), etc. functions
/// since they are shorter.
pub struct Request {
    body: Option<Vec<u8>>,
    follow_redirects: bool,
    handle: Option<Easy>,
    headers: Vec<(String, String)>,
    lowspeed_limits: Option<(u32, Duration)>,
    max_redirects: u32,
    method: Method,
    params: Vec<(String, String)>,
    timeout: Option<Duration>,
    url: Url
}

impl Request {
    /// Creates a new instance of `Request`.
    pub fn new(url: &Url, method: Method) -> Self {
        Request {
            body: None,
            follow_redirects: true,
            handle: None,
            headers: Vec::new(),
            lowspeed_limits: Some((LOW_SPEED_LIMIT, Duration::from_secs(LOW_SPEED_TIME as u64))),
            max_redirects: MAX_REDIRECTS,
            method: method,
            params: Vec::new(),
            timeout: None,
            url: url.clone()
        }
    }

    /// Sets the body of the request as raw byte array.
    pub fn body<B: Into<Vec<u8>>>(mut self, body: B) -> Self {
        self.body = Some(body.into());
        self
    }

    /// Sets the option whether to follow 3xx-redirects or not.
    ///
    /// Defaults to `true`.
    pub fn follow_redirects(mut self, follow: bool) -> Self {
        self.follow_redirects = follow;
        self
    }

    /// Adds an HTTP header to the request.
    pub fn header(mut self, name: &str, value: &str) -> Self {
        self.headers.push((name.to_owned(), value.to_owned()));
        self
    }

    /// Sets the given request headers.
    ///
    /// This overwrites all previously set headers.
    pub fn headers(mut self, headers: Vec<(String, String)>) -> Self {
        self.headers = headers;
        self
    }

    /// Serializes the given object to JSON and uses that as the request body.
    /// Also automatically sets the `Content-Type` to `application/json`.
    ///
    /// ## Panics
    /// Panics if serialization is not successful.
    #[cfg(feature = "rustc-serialization")]
    pub fn json<T: rustc_serialize::Encodable>(self, body: &T) -> Self {
        self.set_json(rustc_serialize::json::encode(body).unwrap().into_bytes())
    }

    /// Serializes the given object to JSON and uses that as the request body.
    /// Also automatically sets the `Content-Type` to `application/json`.
    ///
    /// ## Panics
    /// Panics if serialization is not successful.
    #[cfg(feature = "serde-serialization")]
    pub fn json<T: serde::Serialize>(self, body: &T) -> Self {
        self.set_json(serde_json::to_vec(body).unwrap())
    }

    /// Sets the thresholds which, when reached, aborts a download due to too
    /// low speeds.
    ///
    /// Pass 0 for either parameter to disable lowspeed limiting.
    ///
    /// ## Remarks
    /// `bytes` sets the minimum average amount of bytes transferred in `per_duration`
    /// time. If this number is not reached, cURL will abort the transfer because the transfer
    /// speed is too low.
    ///
    /// The values here default to [`LOW_SPEED_LIMIT`](constant.LOW_SPEED_LIMIT.html) and
    /// [`LOW_SPEED_TIME`](constant.LOW_SPEED_TIME.html).
    pub fn lowspeed_limit(mut self, bytes: u32, per_duration: Duration) -> Self {
        self.lowspeed_limits = if bytes > 0 && per_duration > Duration::from_secs(0) {
            Some((bytes, per_duration))
        } else {
            None
        };
        self
    }

    /// Sets the maximum amount of redirects cURL will follow when
    /// [`Request::follow_redirects`](#method.follow_redirects) is
    /// enabled.
    pub fn max_redirects(mut self, max_redirects: u32) -> Self {
        self.max_redirects = max_redirects;
        self
    }

    /// Adds a URL parameter to the request.
    pub fn param(mut self, name: &str, value: &str) -> Self {
        self.params.push((name.to_owned(), value.to_owned()));
        self
    }

    /// Sets the given request URL parameters.
    ///
    /// This overwrites all previously set parameters.
    pub fn params(mut self, params: Vec<(String, String)>) -> Self {
        self.params = params;
        self
    }

    /// Creates a new `Session` on the specified event loop to send the HTTP request through
    /// and returns a future that fires off the request, parses the response and resolves to
    /// a `Response`-struct on success.
    ///
    /// ## Panics
    /// Panics in case of native exceptions in cURL.
    pub fn send(self, h: Handle) -> BoxFuture<Response, Error> {
        self.send_with_session(&Session::new(h))
    }

    /// Uses the given `Session` to send the HTTP request through and returns a future that
    /// fires off the request, parses the response and resolves to a `Response`-struct on success.
    ///
    /// ## Panics
    /// Panics in case of native exceptions in cURL.
    pub fn send_with_session(mut self, session: &Session) -> BoxFuture<Response, Error> {
        {
            let mut query_pairs = self.url.query_pairs_mut();
            for (key, value) in self.params {
                query_pairs.append_pair(key.trim(), value.trim());
            }
        }
        let headers = {
            let mut list = List::new();
            for (key, value) in self.headers {
                list.append(&format!("{}: {}", key.trim(), value.trim())).expect("Failed to append header value to (native cURL) header list.");
            }
            list
        };

        let mut easy = self.handle.unwrap_or_else(|| Easy::new());
        let (header_tx, header_rx) = channel();
        let (body_tx, body_rx) = channel();

        let config_res = {
            // Make the borrow checker happy
            let body = self.body;
            let follow_redirects = self.follow_redirects;
            let lowspeed_limits = self.lowspeed_limits;
            let max_redirects = self.max_redirects;
            let method = self.method;
            let timeout = self.timeout;
            let url = self.url;
            let mut first_header = true;

            // We cannot use try! here, since we're dealing with futures, not with Results
            Ok(())
                .and_then(|_| easy.accept_encoding(""))
                .and_then(|_| easy.custom_request(method.as_ref()))
                .and_then(|_| if follow_redirects {
                    easy.follow_location(true)
                        .and_then(|_| easy.max_redirections(max_redirects))
                } else {
                    Ok(())
                })
                .and_then(|_| easy.header_function(move |header| {
                    match from_utf8(header) {
                        Ok(s) => {
                            let s = s.trim(); // Headers are \n-separated
                            if !first_header && s.len() > 0 { // First header is HTTP status line, don't want that
                                let _ = header_tx.send(s.to_owned());
                            }
                            first_header = false;
                            true
                        },
                        Err(_) => false
                    }
                }))
                .and_then(|_| easy.http_headers(headers))
                .and_then(|_| if let Some((bytes, per_time)) = lowspeed_limits {
                    easy.low_speed_limit(bytes)
                        .and_then(|_| easy.low_speed_time(per_time))
                } else {
                    Ok(())
                })
                .and_then(|_| if method == Method::Head {
                    easy.nobody(true)
                } else {
                    Ok(())
                })
                .and_then(|_| if let Some(ref body) = body {
                    easy.post_fields_copy(body)
                } else {
                    Ok(())
                })
                .and_then(|_| if let Some(timeout) = timeout {
                    easy.timeout(timeout)
                } else {
                    Ok(())
                })
                .and_then(|_| easy.url(url.as_str()))
                .and_then(|_| easy.write_function(move |data| {
                    let _ = body_tx.send(Vec::from(data));
                    Ok(data.len())
                }))
        };

        match config_res {
            Ok(_) => session.perform(easy)
                            .map_err(|err| err.into_error())
                            .map(move |ez| {
                                let body = body_rx.try_iter().fold(Vec::new(), |mut data, slice| {
                                    data.extend(slice);
                                    data
                                });
                                let headers = header_rx.try_iter().collect::<Vec<_>>();
                                Response::new(ez, headers, body)
                            })
                            .boxed(),
            Err(error) => failed(error.into()).boxed()
        }
    }

    /// Set the maximum time the request is allowed to take.
    ///
    /// Disabled by default in favor of [`lowspeed_limit`]
    pub fn timeout(mut self, duration: Duration) -> Self {
        self.timeout = Some(duration);
        self
    }

    /// Uses the given cURL handle in the request process reusing its resources
    /// and improving performance.
    ///
    /// This is solely a way to improve performance, it is not necessary to call
    /// this method prior to firing off the request. The easy handle will be created
    /// automatically if necessary.
    pub fn use_handle(mut self, handle: Easy) -> Self {
        self.handle = Some(handle);
        self
    }

    #[cfg(any(feature = "rustc-serialization", feature = "serde-serialization"))]
    fn set_json(mut self, body: Vec<u8>) -> Self {
        self.body = Some(body);
        self.header("Content-Type", "application/json")
    }
}

impl Debug for Request {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        let len = if let Some(ref body) = self.body {
            body.len() as isize
        } else {
            -1isize
        };
        fmt.debug_struct(stringify!(Request))
            .field("body_len", &len)
            .field("follow_redirects", &self.follow_redirects)
            .field("headers", &self.headers)
            .field("method", &self.method)
            .field("params", &self.params)
            .field("reuses_handle", &self.handle.is_some())
            .field("url", &self.url)
            .finish()
    }
}

impl Display for Request {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        write!(fmt, "{} {}", self.method, self.url)
    }
}

#[cfg(test)]
mod tests {
    use ::{Method, Request};
    use url::Url;

    #[cfg(feature = "rustc-serialization")]
    use rustc_serialize;

    #[cfg(feature = "serde-serialization")]
    use serde_json;

    #[cfg_attr(feature = "rustc-serialization", derive(RustcEncodable, RustcDecodable))]
    #[cfg_attr(feature = "serde-serialization", derive(Serialize, Deserialize))]
    struct TestPayload {
        a: u32,
        b: u32
    }

    #[test]
    #[cfg(any(feature = "rustc-serialization", feature = "serde-serialization"))]
    fn test_payload() {
        let r = Request::new(&Url::parse("http://google.com/").unwrap(), Method::Get)
            .body(get_serialized_payload());
        assert!(r.body.is_some());
    }

    #[cfg(feature = "rustc-serialization")]
    fn get_serialized_payload() -> Vec<u8> {
        rustc_serialize::json::encode(&TestPayload { a: 10, b: 15 }).unwrap().into_bytes()
    }

    #[cfg(feature = "serde-serialization")]
    fn get_serialized_payload() -> Vec<u8> {
        serde_json::to_vec(&TestPayload { a: 10, b: 15}).unwrap()
    }
}<|endoftext|>


<commit_before>extern crate git2;
const TMP_NAME: &'static str = "refs/centralgit/tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::process::Command;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;

pub struct Scratch<'a>
{
    pub repo: Repository,
    pub host: &'a RepoHost,
}

enum CommitKind
{
    Normal(Oid),
    Merge(Oid, Oid),
    Orphan,
}

impl<'a> Scratch<'a>
{
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a>
    {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    pub fn tracking(&self, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = self.host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        shell.command(&format!("git update-ref {} {}", TMP_NAME, rev));
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, oid: Oid, module: &str, target: &str) -> String
    {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        let cmd = format!("git push {} HEAD:{}", self.host.remote_url(module), target);
        let shell = Shell { cwd: self.repo.path().to_path_buf() };
        let output = shell.command(&cmd);
        debug!("push: {}\n{}", cmd, output);
        format!("{}", output)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = self.subtree(&full_tree, path).unwrap();
            let tree = self.replace_child(name, subtree, &st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue 'walk;
            };

            match match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    match (map.get(&parent1), map.get(&parent2)) {
                        (Some(&parent1), Some(&parent2)) => CommitKind::Merge(parent1, parent2),
                        (Some(&parent), None) => CommitKind::Normal(parent),
                        (None, Some(&parent)) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    match map.get(&parent) {
                        Some(&parent) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                _ => CommitKind::Orphan,
            } {
                CommitKind::Merge(parent1, parent2) => {
                    let parent1 = self.repo.find_commit(parent1).unwrap();
                    let parent2 = self.repo.find_commit(parent2).unwrap();
                    map.insert(commit.id(),
                               self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                    continue 'walk;
                }
                CommitKind::Normal(parent) => {
                    let parent = self.repo.find_commit(parent).unwrap();
                    if new_tree.id() == parent.tree().unwrap().id() {
                        map.insert(commit.id(), parent.id());
                        continue 'walk;
                    }
                    map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    continue 'walk;
                }
                CommitKind::Orphan => {
                    map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
                }
            }
        }

        return map.get(&newrev).map(|&id| id);
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }

    pub fn join(&self, dst: Oid, path: &Path, src: Oid) -> Oid
    {
        let dst = self.repo.find_commit(dst).unwrap();
        let src = self.repo.find_commit(src).unwrap();

        let signature = Signature::new("CentralGit", "cg@cg.com", &dst.committer().when()).unwrap();

        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(src.id()).expect("walk.push");
            walk
        };

        let empty =
            self.repo.find_tree(self.repo.treebuilder(None).unwrap().write().unwrap()).unwrap();
        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");
            let new_tree = self.replace_subtree(path, tree.id(), &empty);

            match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    if let (Some(&parent1), Some(&parent2)) = (map.get(&parent1),
                                                               map.get(&parent2)) {
                        let parent1 = self.repo.find_commit(parent1).unwrap();
                        let parent2 = self.repo.find_commit(parent2).unwrap();

                        let new = new_tree.id();
                        let p1 = parent1.tree().unwrap().id();
                        let p2 = parent2.tree().unwrap().id();

                        map.insert(commit.id(),
                                   self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                        continue 'walk;
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    let parent = *map.get(&parent).unwrap();
                    let parent = self.repo.find_commit(parent).unwrap();
                    map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    continue 'walk;
                }
                _ => {}
            }

            map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
        }

        let final_tree = self.replace_subtree(path, src.tree().unwrap().id(), &dst.tree().unwrap());

        let parents = [&dst, &self.repo.find_commit(map[&src.id()]).unwrap()];
        self.repo.set_head_detached(parents[0].id()).expect("join: can't detach head");
        let join_commit = self.repo
            .commit(Some("HEAD"),
                    &signature,
                    &signature,
                    "repo_join",
                    &final_tree,
                    &parents)
            .unwrap();
        return join_commit;

    }
}
<commit_msg>more cleanup<commit_after>extern crate git2;
const TMP_NAME: &'static str = "refs/centralgit/tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;

pub struct Scratch<'a>
{
    pub repo: Repository,
    pub host: &'a RepoHost,
}

enum CommitKind
{
    Normal(Oid),
    Merge(Oid, Oid),
    Orphan,
}

impl<'a> Scratch<'a>
{
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a>
    {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    pub fn tracking(&self, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = self.host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        shell.command(&format!("git update-ref {} {}", TMP_NAME, rev));
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, oid: Oid, module: &str, target: &str) -> String
    {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        let cmd = format!("git push {} HEAD:{}", self.host.remote_url(module), target);
        let shell = Shell { cwd: self.repo.path().to_path_buf() };
        let output = shell.command(&cmd);
        debug!("push: {}\n{}", cmd, output);
        format!("{}", output)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: &Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = self.subtree(&full_tree, path).unwrap();
            let tree = self.replace_child(name, subtree, &st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue 'walk;
            };

            match match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    match (map.get(&parent1), map.get(&parent2)) {
                        (Some(&parent1), Some(&parent2)) => CommitKind::Merge(parent1, parent2),
                        (Some(&parent), None) => CommitKind::Normal(parent),
                        (None, Some(&parent)) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    match map.get(&parent) {
                        Some(&parent) => CommitKind::Normal(parent),
                        _ => CommitKind::Orphan,
                    }
                }
                _ => CommitKind::Orphan,
            } {
                CommitKind::Merge(parent1, parent2) => {
                    let parent1 = self.repo.find_commit(parent1).unwrap();
                    let parent2 = self.repo.find_commit(parent2).unwrap();
                    map.insert(commit.id(),
                               self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                }
                CommitKind::Normal(parent) => {
                    let parent = self.repo.find_commit(parent).unwrap();
                    if new_tree.id() == parent.tree().unwrap().id() {
                        map.insert(commit.id(), parent.id());
                    }
                    else {
                        map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    }
                }
                CommitKind::Orphan => {
                    map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
                }
            }
        }

        return map.get(&newrev).map(|&id| id);
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }

    pub fn join(&self, dst: Oid, path: &Path, src: Oid) -> Oid
    {
        let dst = self.repo.find_commit(dst).unwrap();
        let src = self.repo.find_commit(src).unwrap();

        let signature = Signature::new("CentralGit", "cg@cg.com", &dst.committer().when()).unwrap();

        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(src.id()).expect("walk.push");
            walk
        };

        let empty =
            self.repo.find_tree(self.repo.treebuilder(None).unwrap().write().unwrap()).unwrap();
        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");
            let new_tree = self.replace_subtree(path, tree.id(), &empty);

            match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    if let (Some(&parent1), Some(&parent2)) = (map.get(&parent1),
                                                               map.get(&parent2)) {
                        let parent1 = self.repo.find_commit(parent1).unwrap();
                        let parent2 = self.repo.find_commit(parent2).unwrap();

                        map.insert(commit.id(),
                                   self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                        continue 'walk;
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    let parent = *map.get(&parent).unwrap();
                    let parent = self.repo.find_commit(parent).unwrap();
                    map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                    continue 'walk;
                }
                _ => {}
            }

            map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
        }

        let final_tree = self.replace_subtree(path, src.tree().unwrap().id(), &dst.tree().unwrap());

        let parents = [&dst, &self.repo.find_commit(map[&src.id()]).unwrap()];
        self.repo.set_head_detached(parents[0].id()).expect("join: can't detach head");
        let join_commit = self.repo
            .commit(Some("HEAD"),
                    &signature,
                    &signature,
                    "repo_join",
                    &final_tree,
                    &parents)
            .unwrap();
        return join_commit;

    }
}
<|endoftext|>


<commit_before>extern crate num;

use std::ops::Mul;
use std::ops::Add;

#[allow(dead_code)]
pub struct ZeroPaddedSignal<T> {
	values: Vec<T>,
}

impl<T: num::traits::Zero + Clone + Mul<Output = T> + Add> ZeroPaddedSignal<T> {
	/**
		Returns the number of initialized values.
	*/
	#[allow(dead_code)]
	pub fn size(&self) -> usize {
		self.values.len()
	}

	/**
		Returns the value at the given index, zero if index
		is negative or uninitialized.
	*/
	#[allow(dead_code)]
	pub fn get(&self, idx: isize) -> T {
		/* Check for negative index: */
		if idx < 0 {
			/* Negative, return zero: */
			T::zero()
		} else {
			/* Not negative, cast to unsigned: */
			let idx = idx as usize;
			/* Check if the index is initialized: */
			if let Some(v) = self.values.get(idx).cloned() {
				/* It is, return the value: */
				v
			} else {
				/* It's not, return zero: */
				T::zero()
			}
		}
	}

	/**
		Returns a vector of signal values, starting with index start,
		ending with index end.
	*/
	#[allow(dead_code)]
	pub fn to_vector(&self, start: isize, end: isize) -> Vec<T> {
		/* Create an empty vector: */
		let mut x: Vec<T> = Vec::new();
		/* Loop through the given range: */
		for i in start..(end+1) {
			/* Add the values to the vector: */
			x.push(self.get(i));
		}
		/* Make the vector immutable: */
		let x = x;
		/* Return the vector: */
		x
	}

	/**
		Creates a new signal by doing Linear Prediction using
		the given coefficients.
	*/
	#[allow(dead_code)]
	pub fn linear_prediction(&self, a: Vec<T>) -> ZeroPaddedSignal<T> {
		let mut vals: Vec<T> = Vec::new();
		let num: usize = self.size() + a.len();
		for i in 0..num {
			let mut val: T = T::zero();
			for j in 0..a.len() {
				let idx = (i - (j+1)) as isize;
				if let Some(v) = a.get(j).cloned() {
					val = val + v*self.get(idx);
				}
			}
			vals.push(val);
		}
		let x: ZeroPaddedSignal<T> = ZeroPaddedSignal { values: vals };
		x
	}
}

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

	#[test]
	fn zero_padded_signal() {
		/* Create test signals: */
		let x1 = ZeroPaddedSignal { values: vec![42,7,11] };
		/* Test size method: */
		assert_eq!( 3, x1.size());
		/* Test get method: */
		assert_eq!( 0, x1.get(  -1));
		assert_eq!(42, x1.get(   0));
		assert_eq!( 7, x1.get(   1));
		assert_eq!(11, x1.get(   2));
		assert_eq!( 0, x1.get(   3));
		assert_eq!( 0, x1.get( 100));
		assert_eq!( 0, x1.get(-100));
		/* Test to_vector method: */
		assert_eq!(vec![0,0,0,42,7,11,0], x1.to_vector(-3,3));
	}
}
<commit_msg>linear_prediction: comments & test<commit_after>extern crate num;

use std::ops::Mul;
use std::ops::Add;

#[allow(dead_code)]
pub struct ZeroPaddedSignal<T> {
	values: Vec<T>,
}

impl<T: num::traits::Zero + Clone + Mul<Output = T> + Add> ZeroPaddedSignal<T> {
	/**
		Returns the number of initialized values.
	*/
	#[allow(dead_code)]
	pub fn size(&self) -> usize {
		self.values.len()
	}

	/**
		Returns the value at the given index, zero if index
		is negative or uninitialized.
	*/
	#[allow(dead_code)]
	pub fn get(&self, idx: isize) -> T {
		/* Check for negative index: */
		if idx < 0 {
			/* Negative, return zero: */
			T::zero()
		} else {
			/* Not negative, cast to unsigned: */
			let idx = idx as usize;
			/* Check if the index is initialized: */
			if let Some(v) = self.values.get(idx).cloned() {
				/* It is, return the value: */
				v
			} else {
				/* It's not, return zero: */
				T::zero()
			}
		}
	}

	/**
		Returns a vector of signal values, starting with index start,
		ending with index end.
	*/
	#[allow(dead_code)]
	pub fn to_vector(&self, start: isize, end: isize) -> Vec<T> {
		/* Create an empty vector: */
		let mut x: Vec<T> = Vec::new();
		/* Loop through the given range: */
		for i in start..(end+1) {
			/* Add the values to the vector: */
			x.push(self.get(i));
		}
		/* Make the vector immutable: */
		let x = x;
		/* Return the vector: */
		x
	}

	/**
		Creates a new signal by doing Linear Prediction using
		the given coefficients.
	*/
	#[allow(dead_code)]
	pub fn linear_prediction(&self, a: Vec<T>) -> ZeroPaddedSignal<T> {
		/* Create a vector for the values: */
		let mut vals: Vec<T> = Vec::new();
		/* Calculate the size of the new signal: */
		let size: usize = self.size() + a.len();
		/* Iterate over the new indices: */
		for i in 0..size {
			let mut val: T = T::zero();
			/* ... and the coefficients: */
			for j in 0..a.len() {
				/* Calculate and typecast the index in the old signal: */
				let idx = (i as isize) - ((j+1) as isize);
				/* Weight the value and add it up: */
				if let Some(v) = a.get(j).cloned() {
					val = val + v*self.get(idx);
				}
			}
			/* Add the value to the vector: */
			vals.push(val);
		}
		/* Create a signal from the vector and return it: */
		let x: ZeroPaddedSignal<T> = ZeroPaddedSignal { values: vals };
		x
	}
}

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

	#[test]
	fn zero_padded_signal() {
		/* Create test signals: */
		let x1: ZeroPaddedSignal<u32> = ZeroPaddedSignal { values: vec![42,7,11] };
		/* Test size method: */
		assert_eq!( 3, x1.size());
		/* Test get method: */
		assert_eq!( 0, x1.get(  -1));
		assert_eq!(42, x1.get(   0));
		assert_eq!( 7, x1.get(   1));
		assert_eq!(11, x1.get(   2));
		assert_eq!( 0, x1.get(   3));
		assert_eq!( 0, x1.get( 100));
		assert_eq!( 0, x1.get(-100));
		/* Test to_vector method: */
		assert_eq!(vec![0,0,0,42,7,11,0], x1.to_vector(-3,3));
	}

	#[test]
	fn linear_prediction() {
		let x2: ZeroPaddedSignal<f64> = ZeroPaddedSignal { values: vec![1.,1.,1.,1.,1.,1.] };
		/* Test linear_prediction method: */
		assert_eq!(vec![0., 0.8888888888888888, 0.8888888888888888, 0.8888888888888888, 0.7777777777777777, 0.7777777777777777, 0.7777777777777777, -0.1111111111111111, -0.1111111111111111, -0.1111111111111111], x2.linear_prediction(vec![8./9.,0.,0.,-1./9.]).values);
	}
}
<|endoftext|>