<commit_before>use std::io::{self, Write};
use std::iter::repeat;
use std::time::Duration;
use time::{self, SteadyTime};
use std::io::Stdout;
use tty::{Width, terminal_size};

macro_rules! printfl {
   ($w:expr, $($tt:tt)*) => {{
        $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail");
        $w.flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

const FORMAT: &'static str = "[=>-]";
const TICK_FORMAT: &'static str = "\\|/-";
const NANOS_PER_SEC: u32 = 1_000_000_000;

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

pub struct ProgressBar<T: Write> {
    start_time: SteadyTime,
    units: Units,
    pub total: u64,
    current: u64,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    tick: Vec<String>,
    tick_state: usize,
    tick_len: usize,
    message: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
    pub show_tick: bool,
    pub show_message: bool,
    handle: T,
}

impl ProgressBar<Stdout> {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: u64) -> ProgressBar<Stdout> {
        let handle = ::std::io::stdout();
        ProgressBar::on(handle, total)
    }
}

impl<T: Write> ProgressBar<T> {
    /// Create a new ProgressBar with default configuration but
    /// pass an arbitrary writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use std::io::stderr;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::on(stderr(), count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn on(handle: T, total: u64) -> ProgressBar<T> {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: SteadyTime::now(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            show_tick: false,
            show_message: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
            tick: Vec::new(),
            tick_state: 0,
            tick_len: 4,
            message: String::new(),
            handle: handle,
        };
        pb.format(FORMAT);
        pb.tick_format(TICK_FORMAT);
        pb
    }

    /// Set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let n_bytes = 100;
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_owned();
            self.bar_current = v[2].to_owned();
            self.bar_current_n = v[3].to_owned();
            self.bar_remain = v[4].to_owned();
            self.bar_end = v[5].to_owned();
        }
    }

    /// Set message to display in the prefix, call with "" to stop printing a message.
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(20);
    ///
    /// for x in 0..20 {
    ///    match x {
    ///       0 => pb.message("Doing 1st Quarter"),
    ///       5 => pb.message("Doing 2nd Quarter"),
    ///       10 => pb.message("Doing 3rd Quarter"),
    ///       15 => pb.message("Doing 4th Quarter"),
    ///    }
    ///    pb.inc().
    /// }
    ///
    /// ```
    pub fn message(&mut self, message: &str) {
        self.message = message.to_owned()
    }

    /// Set tick format for the progressBar, default is \\|/-
    ///
    /// Format is not limited to 4 characters, any string can
    /// be used as a tick format (the tick will successively
    /// take the value of each char but won't loop backwards).
    ///
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.tick_format("▀▐▄▌")
    /// ```
    pub fn tick_format(&mut self, tick_fmt: &str) {
        if tick_fmt != TICK_FORMAT {
            self.show_tick = true;
        };
        self.tick = tick_fmt.split("").map(|x| x.to_owned()).filter(|x| x != "").collect();
        self.tick_len = self.tick.len();
    }

    /// Update progress bar even though no progress are made
    /// Useful to see if a program is bricked or just
    /// not doing any progress.
    ///
    /// tick is not needed with add or inc
    /// as performed operation take place
    /// in draw function.
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.inc();
    /// for _ in ... {
    ///    ...do something
    ///    pb.tick();
    /// }
    /// pb.finish();
    /// ```
    pub fn tick(&mut self) {
        self.tick_state = (self.tick_state + 1) % self.tick_len;
        if self.current <= self.total {
            self.draw()
        };
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::ProgressBar;
    ///
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: u64) -> u64 {
        self.current += i;
        self.tick();
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> u64 {
        self.add(1)
    }

    fn draw(&mut self) {
        let time_elapsed = time_to_std(SteadyTime::now() - self.start_time);
        let speed = self.current as f64 / fract_dur(time_elapsed);

        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;

        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left && self.current > 0 {
            if self.total > self.current {
                let left = 1. / speed * (self.total - self.current) as f64;
                if left < 60. {
                    suffix = suffix + &format!("{:.0}s", left);
                } else {
                    suffix = suffix + &format!("{:.0}m", left / 60.);
                }
            }
        }
        // message box
        if self.show_message {
            prefix = prefix + &format!("{}", self.message)
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = prefix +
                     &match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // tick box
        if self.show_tick {
            prefix = prefix + &format!("{} ", self.tick[self.tick_state]);
        }
        // bar box
        if self.show_bar {
            let p = prefix.len() + suffix.len() + 3;
            if p < width {
                let size = width - p;
                let curr_count = ((self.current as f64 / self.total as f64) * size as f64)
                    .ceil() as usize;
                if size >= curr_count {
                    let rema_count = size - curr_count;
                    base = self.bar_start.clone();
                    if rema_count > 0 && curr_count > 0 {
                        base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                                &self.bar_current_n;
                    } else {
                        base = base + repeat!(self.bar_current.as_ref(), curr_count);
                    }
                    base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
                }
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!(self.handle, "\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        printfl!(self.handle, "");
        self.is_finish = true;
    }

    /// Call finish and write string 's'
    pub fn finish_print(&mut self, s: &str) {
        self.finish();
        printfl!(self.handle, "{}", s)
    }
}

// Implement io::Writer
impl<T: Write> Write for ProgressBar<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n as u64);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

fn time_to_std(d: time::Duration) -> Duration {
    assert!(d > time::Duration::zero());

    let secs = d.num_seconds();
    let nsecs = (d - time::Duration::seconds(secs)).num_nanoseconds().unwrap();
    Duration::new(secs as u64, nsecs as u32)
}

fn fract_dur(d: Duration) -> f64 {
    d.as_secs() as f64 + d.subsec_nanos() as f64 / NANOS_PER_SEC as f64
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2, "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total, "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<commit_msg>fix(pb): add newline prefix in finish_print<commit_after>use std::io::{self, Write};
use std::iter::repeat;
use std::time::Duration;
use time::{self, SteadyTime};
use std::io::Stdout;
use tty::{Width, terminal_size};

macro_rules! printfl {
   ($w:expr, $($tt:tt)*) => {{
        $w.write(&format!($($tt)*).as_bytes()).ok().expect("write() fail");
        $w.flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)),
            $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)),
            $n if $n >= kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &repeat($s).take($n).collect::<String>()
    }}
}

const FORMAT: &'static str = "[=>-]";
const TICK_FORMAT: &'static str = "\\|/-";
const NANOS_PER_SEC: u32 = 1_000_000_000;

// Output type format, indicate which format wil be used in
// the speed box.
#[derive(Debug)]
pub enum Units {
    Default,
    Bytes,
}

pub struct ProgressBar<T: Write> {
    start_time: SteadyTime,
    units: Units,
    pub total: u64,
    current: u64,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
    tick: Vec<String>,
    tick_state: usize,
    tick_len: usize,
    message: String,
    pub is_finish: bool,
    pub show_bar: bool,
    pub show_speed: bool,
    pub show_percent: bool,
    pub show_counter: bool,
    pub show_time_left: bool,
    pub show_tick: bool,
    pub show_message: bool,
    handle: T,
}

impl ProgressBar<Stdout> {
    /// Create a new ProgressBar with default configuration.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::new(count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn new(total: u64) -> ProgressBar<Stdout> {
        let handle = ::std::io::stdout();
        ProgressBar::on(handle, total)
    }
}

impl<T: Write> ProgressBar<T> {
    /// Create a new ProgressBar with default configuration but
    /// pass an arbitrary writer.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::thread;
    /// use std::io::stderr;
    /// use pbr::{ProgressBar, Units};
    ///
    /// let count = 1000;
    /// let mut pb = ProgressBar::on(stderr(), count);
    /// pb.set_units(Units::Bytes);
    ///
    /// for _ in 0..count {
    ///    pb.inc();
    ///    thread::sleep_ms(100);
    /// }
    /// ```
    pub fn on(handle: T, total: u64) -> ProgressBar<T> {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: SteadyTime::now(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: true,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            show_tick: false,
            show_message: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
            tick: Vec::new(),
            tick_state: 0,
            tick_len: 4,
            message: String::new(),
            handle: handle,
        };
        pb.format(FORMAT);
        pb.tick_format(TICK_FORMAT);
        pb
    }

    /// Set units, default is simple numbers
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::{ProgressBar, Units};
    ///
    /// let n_bytes = 100;
    /// let mut pb = ProgressBar::new(n_bytes);
    /// pb.set_units(Units::Bytes);
    /// ```
    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    /// Set custom format to the drawing bar, default is `[=>-]`
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.format("[=>_]");
    /// ```
    pub fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_owned();
            self.bar_current = v[2].to_owned();
            self.bar_current_n = v[3].to_owned();
            self.bar_remain = v[4].to_owned();
            self.bar_end = v[5].to_owned();
        }
    }

    /// Set message to display in the prefix, call with "" to stop printing a message.
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(20);
    ///
    /// for x in 0..20 {
    ///    match x {
    ///       0 => pb.message("Doing 1st Quarter"),
    ///       5 => pb.message("Doing 2nd Quarter"),
    ///       10 => pb.message("Doing 3rd Quarter"),
    ///       15 => pb.message("Doing 4th Quarter"),
    ///    }
    ///    pb.inc().
    /// }
    ///
    /// ```
    pub fn message(&mut self, message: &str) {
        self.message = message.to_owned()
    }

    /// Set tick format for the progressBar, default is \\|/-
    ///
    /// Format is not limited to 4 characters, any string can
    /// be used as a tick format (the tick will successively
    /// take the value of each char but won't loop backwards).
    ///
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.tick_format("▀▐▄▌")
    /// ```
    pub fn tick_format(&mut self, tick_fmt: &str) {
        if tick_fmt != TICK_FORMAT {
            self.show_tick = true;
        };
        self.tick = tick_fmt.split("").map(|x| x.to_owned()).filter(|x| x != "").collect();
        self.tick_len = self.tick.len();
    }

    /// Update progress bar even though no progress are made
    /// Useful to see if a program is bricked or just
    /// not doing any progress.
    ///
    /// tick is not needed with add or inc
    /// as performed operation take place
    /// in draw function.
    ///
    /// # Examples
    /// ```ignore
    /// let mut pb = ProgressBar::new(...);
    /// pb.inc();
    /// for _ in ... {
    ///    ...do something
    ///    pb.tick();
    /// }
    /// pb.finish();
    /// ```
    pub fn tick(&mut self) {
        self.tick_state = (self.tick_state + 1) % self.tick_len;
        if self.current <= self.total {
            self.draw()
        };
    }

    /// Add to current value
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use pbr::ProgressBar;
    ///
    /// let mut pb = ProgressBar::new(10);
    /// pb.add(5);
    /// pb.finish();
    /// ```
    pub fn add(&mut self, i: u64) -> u64 {
        self.current += i;
        self.tick();
        self.current
    }

    /// Increment current value
    pub fn inc(&mut self) -> u64 {
        self.add(1)
    }

    fn draw(&mut self) {
        let time_elapsed = time_to_std(SteadyTime::now() - self.start_time);
        let speed = self.current as f64 / fract_dur(time_elapsed);

        let tty_size = terminal_size();
        let width = if let Some((Width(w), _)) = tty_size {
            w as usize
        } else {
            80
        };
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;

        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if self.show_speed {
            suffix = match self.units {
                Units::Default => suffix + &format!("{:.*}/s ", 2, speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left && self.current > 0 {
            if self.total > self.current {
                let left = 1. / speed * (self.total - self.current) as f64;
                if left < 60. {
                    suffix = suffix + &format!("{:.0}s", left);
                } else {
                    suffix = suffix + &format!("{:.0}m", left / 60.);
                }
            }
        }
        // message box
        if self.show_message {
            prefix = prefix + &format!("{}", self.message)
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = prefix +
                     &match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // tick box
        if self.show_tick {
            prefix = prefix + &format!("{} ", self.tick[self.tick_state]);
        }
        // bar box
        if self.show_bar {
            let p = prefix.len() + suffix.len() + 3;
            if p < width {
                let size = width - p;
                let curr_count = ((self.current as f64 / self.total as f64) * size as f64)
                    .ceil() as usize;
                if size >= curr_count {
                    let rema_count = size - curr_count;
                    base = self.bar_start.clone();
                    if rema_count > 0 && curr_count > 0 {
                        base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                                &self.bar_current_n;
                    } else {
                        base = base + repeat!(self.bar_current.as_ref(), curr_count);
                    }
                    base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
                }
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!(self.handle, "\r{}", out);
    }

    /// Calling finish manually will set current to total and draw
    /// the last time
    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        printfl!(self.handle, "");
        self.is_finish = true;
    }

    /// Call finish and write string 's'
    pub fn finish_print(&mut self, s: &str) {
        self.finish();
        printfl!(self.handle, "\n{}", s)
    }
}

// Implement io::Writer
impl<T: Write> Write for ProgressBar<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n as u64);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

fn time_to_std(d: time::Duration) -> Duration {
    assert!(d > time::Duration::zero());

    let secs = d.num_seconds();
    let nsecs = (d - time::Duration::seconds(secs)).num_nanoseconds().unwrap();
    Duration::new(secs as u64, nsecs as u32)
}

fn fract_dur(d: Duration) -> f64 {
    d.as_secs() as f64 + d.subsec_nanos() as f64 / NANOS_PER_SEC as f64
}

#[cfg(test)]
mod test {
    use pb::ProgressBar;

    #[test]
    fn add() {
        let mut pb = ProgressBar::new(10);
        pb.add(2);
        assert!(pb.current == 2, "should add the given `n` to current");
        assert!(pb.add(2) == pb.current,
                "add should return the current value");
    }

    #[test]
    fn inc() {
        let mut pb = ProgressBar::new(10);
        pb.inc();
        assert!(pb.current == 1, "should increment current by 1");
    }

    #[test]
    fn format() {
        let fmt = "[~> ]";
        let mut pb = ProgressBar::new(1);
        pb.format(fmt);
        assert!(pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain +
                &pb.bar_end == fmt);
    }

    #[test]
    fn finish() {
        let mut pb = ProgressBar::new(10);
        pb.finish();
        assert!(pb.current == pb.total, "should set current to total");
        assert!(pb.is_finish, "should set is_finish to true");
    }

    #[test]
    fn kb_fmt() {
        let kb = 1024f64;
        let mb = kb.powf(2f64);
        let gb = kb.powf(3f64);
        let tb = kb.powf(4f64);
        assert_eq!(kb_fmt!(kb), "1.00 KB");
        assert_eq!(kb_fmt!(mb), "1.00 MB");
        assert_eq!(kb_fmt!(gb), "1.00 GB");
        assert_eq!(kb_fmt!(tb), "1.00 TB");
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-test
struct Obj {
    member: uint
}

pub impl Obj {
    fn boom() -> bool {
        return 1+1 == 2
    }
    fn chirp() {
        self.boom(); //~ ERROR wat
    }
}

fn main() {
    let o = Obj { member: 0 };
    o.chirp();
    1 + 1;
}
<commit_msg>auto merge of #6290 : brson/rust/issue-3707, r=thestinger<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Obj {
    member: uint
}

pub impl Obj {
    fn boom() -> bool {
        return 1+1 == 2
    }
    fn chirp(&self) {
        self.boom(); //~ ERROR `&Obj` does not implement any method in scope named `boom`
    }
}

fn main() {
    let o = Obj { member: 0 };
    o.chirp();
    1 + 1;
}
<|endoftext|>


<commit_before>extern crate elektra_sys;

pub use elektra_sys::*;
<commit_msg>Rust: Implement first parts of safe Rust API<commit_after>extern crate elektra_sys;

use elektra_sys::*;
use std::boxed::Box;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt;

trait KeyBuilder {
    fn new() -> Self;
    fn name<T: Into<Vec<u8>>>(&mut self, name: T) -> Self;
    fn build(self) -> Key;
}

#[derive(Debug)]
enum KeyError {
    InvalidName,
    // TypeMismatch,
    // TypeConversion,
    // NotFoundException
}

impl fmt::Display for KeyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        match self {
            KeyError::InvalidName => write!(f, "Key has an invalid name"),
            // _ => unimplemented!(),
        }
    }
}

impl Error for KeyError {}

trait KeyProperties {
    fn new() -> Box<Key>;
    fn set_name(&mut self, name: &str) -> Result<u32, KeyError>;
    fn name(&self) -> &str;
    fn duplicate(&self) -> Box<Key>;
}

impl KeyProperties for Key {
    /// Initializes an empty key object
    fn new() -> Box<Key> {
        unsafe { Box::from_raw(keyNew(0 as *const i8)) }
    }

    /// Set the name of a key
    /// # Examples
    /// ```
    /// let key = Key::new().name("user/test/rust");
    /// assert_eq!(key.get_name(), "user/test/rust");
    /// ```
    fn set_name(&mut self, name: &str) -> Result<u32, KeyError> {
        let cptr = CString::new(name).unwrap();
        let ret_val = unsafe { keySetName(self, cptr.as_ptr()) };

        if ret_val > 0 {
            Ok(ret_val as u32)
        } else {
            Err(KeyError::InvalidName)
        }
    }

    /// Return the name of the key as a borrowed slice
    fn name(&self) -> &str {
        let c_str = unsafe { CStr::from_ptr(keyName(self)) };
        c_str.to_str().unwrap()
    }

    /// Return a duplicate of a key
    fn duplicate(&self) -> Box<Key> {
        unsafe { Box::from_raw(keyDup(self)) }
    }
}

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

    #[test]
    fn can_write_read_key() {
        let key_name = "user/test/key";
        let mut key = Key::new();
        key.set_name(key_name).unwrap();
        assert_eq!(key.name(), key_name);
    }

    #[test]
    fn can_duplicate_key() {
        let key_name = "user/test/key";
        let mut key_dup;
        {
            let mut key = Key::new();
            key.set_name(key_name).unwrap();
            key_dup = Some(key.duplicate());
            // key is dropped here
        }
        assert_eq!(key_dup.unwrap().name(), key_name);
    }
}
<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::marker::PhantomData;

use llvm_sys::prelude::*;
use llvm_sys::core::*;

use owned::{Owned, DropInPlace};

use llvm::{Context, Type, Function, FunctionLabel};

pub struct Module<'cid: 'context, 'context> {
    _context: PhantomData<&'context Context<'cid>>
}

impl<'cid, 'context> DropInPlace for Module<'cid, 'context> {
    unsafe fn drop_in_place(&mut self) {
        LLVMDisposeModule(self.as_raw());
    }
}

impl<'cid, 'context> Module<'cid, 'context> {
    pub fn new(name: &CStr, context: &'context Context<'cid>) -> Owned<Module<'cid, 'context>> {
        unsafe {
            Owned::from_raw(
                LLVMModuleCreateWithNameInContext(name.as_ptr(), context.as_raw()) as *mut Module
            )
        }
    }

    pub fn builder<'module>(&'module mut self) -> ModuleBuilder<'cid, 'context, 'module> {
        ModuleBuilder {
            inner: self
        }
    }

    pub fn as_raw(&self) -> LLVMModuleRef {
        self as *const Module as *mut Module as LLVMModuleRef
    }
}

pub struct ModuleBuilder<'cid: 'context, 'context: 'module, 'module> {
    inner: &'module mut Module<'cid, 'context>
}

impl<'cid, 'context, 'module> ModuleBuilder<'cid, 'context, 'module> {
    pub fn add_function<'function>(&mut self, name: &CStr, ty: &Type<'cid>) -> &'function mut Function<'cid> where 'module: 'function {
        unsafe {
            &mut *(LLVMAddFunction(self.inner.as_raw(), name.as_ptr(), ty.as_raw()) as *mut Function)
        }
    }

    pub fn get_named_function(&self, name: &CStr) -> Option<&'module FunctionLabel<'cid>> {
        unsafe {
            let old = LLVMGetNamedFunction(self.inner.as_raw(), name.as_ptr());
            if old.is_null() {
                None
            } else {
                Some(&*(old as *mut FunctionLabel))
            }
        }
    }
}
<commit_msg>Simplify signature of add_function<commit_after>use std::ffi::CStr;
use std::marker::PhantomData;

use llvm_sys::prelude::*;
use llvm_sys::core::*;

use owned::{Owned, DropInPlace};

use llvm::{Context, Type, Function, FunctionLabel};

pub struct Module<'cid: 'context, 'context> {
    _context: PhantomData<&'context Context<'cid>>
}

impl<'cid, 'context> DropInPlace for Module<'cid, 'context> {
    unsafe fn drop_in_place(&mut self) {
        LLVMDisposeModule(self.as_raw());
    }
}

impl<'cid, 'context> Module<'cid, 'context> {
    pub fn new(name: &CStr, context: &'context Context<'cid>) -> Owned<Module<'cid, 'context>> {
        unsafe {
            Owned::from_raw(
                LLVMModuleCreateWithNameInContext(name.as_ptr(), context.as_raw()) as *mut Module
            )
        }
    }

    pub fn builder<'module>(&'module mut self) -> ModuleBuilder<'cid, 'context, 'module> {
        ModuleBuilder {
            inner: self
        }
    }

    pub fn as_raw(&self) -> LLVMModuleRef {
        self as *const Module as *mut Module as LLVMModuleRef
    }
}

pub struct ModuleBuilder<'cid: 'context, 'context: 'module, 'module> {
    inner: &'module mut Module<'cid, 'context>
}

impl<'cid, 'context, 'module> ModuleBuilder<'cid, 'context, 'module> {
    pub fn add_function(&mut self, name: &CStr, ty: &Type<'cid>) -> &'module mut Function<'cid> {
        unsafe {
            &mut *(LLVMAddFunction(self.inner.as_raw(), name.as_ptr(), ty.as_raw()) as *mut Function)
        }
    }

    pub fn get_named_function(&self, name: &CStr) -> Option<&'module FunctionLabel<'cid>> {
        unsafe {
            let old = LLVMGetNamedFunction(self.inner.as_raw(), name.as_ptr());
            if old.is_null() {
                None
            } else {
                Some(&*(old as *mut FunctionLabel))
            }
        }
    }
}
<|endoftext|>


<commit_before>use calx_alg::split_line;
use euclid::{Point2D, Rect};
use std::io;
use std::io::prelude::*;
use std::mem;
use std::str;
use time;
use vitral::{Align, Context};

struct Message {
    expire_time_s: f64,
    text: String,
}

impl Message {
    fn new(text: String, time_start_s: f64) -> Message {
        const TIME_TO_READ_CHAR_S: f64 = 0.2;
        let expire_time_s = time_start_s + text.len() as f64 * TIME_TO_READ_CHAR_S;
        Message {
            expire_time_s: expire_time_s,
            text: text,
        }
    }
}

/// Output text container.
#[derive(Default)]
pub struct Console {
    lines: Vec<Message>,
    input_buffer: String,
    output_buffer: String,
    done_reading_s: f64,
}

impl Console {
    /// Draw the console as a regular message display.
    pub fn draw_small<C: Context>(&mut self, context: &mut C, screen_area: &Rect<f32>) {
        // TODO: Store color in draw context.
        let color = [1.0, 1.0, 1.0, 1.0];

        let t = time::precise_time_s();
        let h = context.current_font().height;
        let mut y = screen_area.max_y() - h;
        // The log can be very long, and we're always most interested in the latest ones, so
        // do a backwards iteration with an early exist once we hit a sufficiently old item.
        for msg in self.lines.iter().rev().take_while(|m| m.expire_time_s > t) {
            // The split_line iterator can't be reversed, need to do a bit of caching here.
            let fragments = split_line(
                &msg.text,
                |c| context.current_font().char_width(c).unwrap_or(0.0),
                screen_area.size.width,
            ).map(|x| x.to_string())
                .collect::<Vec<String>>();
            for line in fragments.iter().rev() {
                context.draw_text(Point2D::new(0.0, y), Align::Left, color, line);
                y -= h;
            }
        }
    }

    /// Draw the console as a big drop-down with a command prompt.
    pub fn draw_large<C: Context>(&mut self, context: &mut C, screen_area: &Rect<f32>) {
        // TODO: Store color in draw context.
        let color = [0.6, 0.6, 0.6, 1.0];
        let background = [0.0, 0.0, 0.6, 0.8];

        context.fill_rect(*screen_area, background);

        let h = context.current_font().height;
        let mut lines_left = (screen_area.size.height / h).ceil() as i32;
        let mut y = screen_area.max_y() - h;

        // TODO: Handle enter with text input.
        // TODO: Command history.
        context
            .bound(0, y as u32, screen_area.size.width as u32, h as u32)
            .text_input(color, &mut self.input_buffer);
        y -= h;
        lines_left -= 1;

        for msg in self.lines.iter().rev() {
            // XXX: Duplicated from draw_small.
            let fragments = split_line(
                &msg.text,
                |c| context.current_font().char_width(c).unwrap_or(0.0),
                screen_area.size.width,
            ).map(|x| x.to_string())
                .collect::<Vec<String>>();
            for line in fragments.iter().rev() {
                context.draw_text(Point2D::new(0.0, y), Align::Left, color, line);
                y -= h;
                lines_left -= 1;
            }

            if lines_left <= 0 {
                break;
            }
        }
    }

    fn end_message(&mut self) {
        let mut message_text = String::new();
        mem::swap(&mut message_text, &mut self.output_buffer);

        let now = time::precise_time_s();
        if now > self.done_reading_s {
            self.done_reading_s = now;
        }

        let message = Message::new(message_text, self.done_reading_s);
        assert!(message.expire_time_s >= self.done_reading_s);
        self.done_reading_s = message.expire_time_s;
        self.lines.push(message);
    }

    pub fn get_input(&mut self) -> String {
        let mut ret = String::new();
        mem::swap(&mut ret, &mut self.input_buffer);
        ret
    }
}

impl Write for Console {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let s = str::from_utf8(buf).expect("Wrote non-UTF-8 to Console");
        let mut lines = s.split('\n');
        lines.next().map(|text| self.output_buffer.push_str(text));

        for line in lines {
            self.end_message();
            self.output_buffer.push_str(line);
        }

        Ok(buf.len())
    }

    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
<commit_msg>Console text is transparent, can see tiles under it<commit_after>use calx_alg::split_line;
use euclid::{Point2D, Rect};
use std::io;
use std::io::prelude::*;
use std::mem;
use std::str;
use time;
use vitral::{Align, Context};

struct Message {
    expire_time_s: f64,
    text: String,
}

impl Message {
    fn new(text: String, time_start_s: f64) -> Message {
        const TIME_TO_READ_CHAR_S: f64 = 0.2;
        let expire_time_s = time_start_s + text.len() as f64 * TIME_TO_READ_CHAR_S;
        Message {
            expire_time_s: expire_time_s,
            text: text,
        }
    }
}

/// Output text container.
#[derive(Default)]
pub struct Console {
    lines: Vec<Message>,
    input_buffer: String,
    output_buffer: String,
    done_reading_s: f64,
}

impl Console {
    /// Draw the console as a regular message display.
    pub fn draw_small<C: Context>(&mut self, context: &mut C, screen_area: &Rect<f32>) {
        // TODO: Store color in draw context.
        let color = [1.0, 1.0, 1.0, 0.4];

        let t = time::precise_time_s();
        let h = context.current_font().height;
        let mut y = screen_area.max_y() - h;
        // The log can be very long, and we're always most interested in the latest ones, so
        // do a backwards iteration with an early exist once we hit a sufficiently old item.
        for msg in self.lines.iter().rev().take_while(|m| m.expire_time_s > t) {
            // The split_line iterator can't be reversed, need to do a bit of caching here.
            let fragments = split_line(
                &msg.text,
                |c| context.current_font().char_width(c).unwrap_or(0.0),
                screen_area.size.width,
            ).map(|x| x.to_string())
                .collect::<Vec<String>>();
            for line in fragments.iter().rev() {
                context.draw_text(Point2D::new(0.0, y), Align::Left, color, line);
                y -= h;
            }
        }
    }

    /// Draw the console as a big drop-down with a command prompt.
    pub fn draw_large<C: Context>(&mut self, context: &mut C, screen_area: &Rect<f32>) {
        // TODO: Store color in draw context.
        let color = [0.6, 0.6, 0.6, 1.0];
        let background = [0.0, 0.0, 0.6, 0.8];

        context.fill_rect(*screen_area, background);

        let h = context.current_font().height;
        let mut lines_left = (screen_area.size.height / h).ceil() as i32;
        let mut y = screen_area.max_y() - h;

        // TODO: Handle enter with text input.
        // TODO: Command history.
        context
            .bound(0, y as u32, screen_area.size.width as u32, h as u32)
            .text_input(color, &mut self.input_buffer);
        y -= h;
        lines_left -= 1;

        for msg in self.lines.iter().rev() {
            // XXX: Duplicated from draw_small.
            let fragments = split_line(
                &msg.text,
                |c| context.current_font().char_width(c).unwrap_or(0.0),
                screen_area.size.width,
            ).map(|x| x.to_string())
                .collect::<Vec<String>>();
            for line in fragments.iter().rev() {
                context.draw_text(Point2D::new(0.0, y), Align::Left, color, line);
                y -= h;
                lines_left -= 1;
            }

            if lines_left <= 0 {
                break;
            }
        }
    }

    fn end_message(&mut self) {
        let mut message_text = String::new();
        mem::swap(&mut message_text, &mut self.output_buffer);

        let now = time::precise_time_s();
        if now > self.done_reading_s {
            self.done_reading_s = now;
        }

        let message = Message::new(message_text, self.done_reading_s);
        assert!(message.expire_time_s >= self.done_reading_s);
        self.done_reading_s = message.expire_time_s;
        self.lines.push(message);
    }

    pub fn get_input(&mut self) -> String {
        let mut ret = String::new();
        mem::swap(&mut ret, &mut self.input_buffer);
        ret
    }
}

impl Write for Console {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let s = str::from_utf8(buf).expect("Wrote non-UTF-8 to Console");
        let mut lines = s.split('\n');
        lines.next().map(|text| self.output_buffer.push_str(text));

        for line in lines {
            self.end_message();
            self.output_buffer.push_str(line);
        }

        Ok(buf.len())
    }

    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

use common::{
    eyre::{self, bail, Result},
    serde::Serialize,
    strum::AsRefStr,
};
use graph_triples::ResourceInfo;

/// An execution plan for a document
#[derive(Debug, Default, Serialize)]
#[serde(crate = "common::serde")]
pub struct Plan {
    /// The options used to generate the plan
    pub options: PlanOptions,

    /// The stages to be executed
    pub stages: Vec<PlanStage>,
}

impl Plan {
    pub fn to_markdown(&self) -> String {
        let options = self.options.to_markdown();
        let stages = self
            .stages
            .iter()
            .enumerate()
            .map(|(index, stage)| format!("## Stage {}\n\n{}", index + 1, stage.to_markdown()))
            .collect::<Vec<String>>()
            .join("\n\n");
        format!("{}\n\n{}", options, stages)
    }
}

/// Options for generating a plan
#[derive(Debug, Clone, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanOptions {
    /// The ordering of nodes used when generating the plan
    pub ordering: PlanOrdering,

    /// The maximum task concurrency
    ///
    /// Limits the number of tasks that can be grouped together in a stage.
    /// Defaults to the number of logical CPU cores on the current machine.
    pub max_concurrency: usize,
}

impl PlanOptions {
    pub fn default_ordering() -> PlanOrdering {
        PlanOrdering::Topological
    }

    pub fn default_max_concurrency() -> usize {
        num_cpus::get()
    }

    pub fn to_markdown(&self) -> String {
        format!(
            r"## Options

- Ordering: {}
- Maximum concurrency: {}",
            self.ordering.as_ref(),
            self.max_concurrency
        )
    }
}

impl Default for PlanOptions {
    fn default() -> Self {
        Self {
            ordering: Self::default_ordering(),
            max_concurrency: Self::default_max_concurrency(),
        }
    }
}

/// The ordering of nodes used when generating a plan
#[derive(Debug, Clone, Serialize, AsRefStr)]
#[serde(crate = "common::serde")]
pub enum PlanOrdering {
    /// Only a single, specified, node is to be executed
    Single,

    /// Nodes are executed in the order that they appear in the
    /// document, top to bottom, left to right.
    Appearance,

    /// Nodes are executed in the order that ensures
    /// that the dependencies of a node are executed before it is
    Topological,
}

impl FromStr for PlanOrdering {
    type Err = eyre::Report;

    fn from_str(str: &str) -> Result<PlanOrdering> {
        Ok(match str.to_lowercase().as_str() {
            "s" | "si" | "sin" | "single" => PlanOrdering::Single,
            "a" | "ap" | "app" | "appear" | "appearance" => PlanOrdering::Appearance,
            "t" | "to" | "top" | "topo" | "topological" => PlanOrdering::Topological,
            _ => bail!("Unrecognized plan ordering: {}", str),
        })
    }
}

/// The scope of a cancellation request
#[derive(Debug, Clone, Serialize, AsRefStr)]
#[serde(crate = "common::serde")]
pub enum PlanScope {
    /// A single node in the current execution plan
    Single,

    /// All nodes in the current execution plan
    All,
}

impl FromStr for PlanScope {
    type Err = eyre::Report;

    fn from_str(str: &str) -> Result<PlanScope> {
        Ok(match str.to_lowercase().as_str() {
            "s" | "si" | "sin" | "single" => PlanScope::Single,
            "a" | "all" => PlanScope::All,
            _ => bail!("Unrecognized plan scope: {}", str),
        })
    }
}

/// A stage in an execution plan
///
/// A stage represents a group of [`PlanTask`]s that can be executed concurrently
/// (e.g. because they can be executed in different kernels or a kernel fork)
#[derive(Debug, Default, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanStage {
    /// The tasks to be executed
    pub tasks: Vec<PlanTask>,
}

impl PlanStage {
    pub fn to_markdown(&self) -> String {
        self.tasks
            .iter()
            .map(|task| ["- ", &task.to_markdown()].concat())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

/// A task in an execution plan
///
/// A task is the smallest unit in an execution plan and corresponds to a kernel [`Task`]
#[derive(Debug, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanTask {
    /// Information on the resource to be executed
    ///
    /// Passed to kernel for `symbols_used` etc
    pub resource_info: ResourceInfo,

    /// The name of the kernel that the code will be executed in
    ///
    /// If this is `None` it indicates that no kernel capable of executing
    /// the node is available on the machine
    pub kernel_name: Option<String>,

    /// The code will be executed in a fork of the kernel
    ///
    /// Code that has no side effects or who's side effects should be
    /// ignored (i.e. is "@pure") are executed in a fork of the kernel.
    pub is_fork: bool,
}

impl PlanTask {
    pub fn to_markdown(&self) -> String {
        let node_type = self.resource_info.resource.node_type().unwrap_or("?");
        let node_id = self.resource_info.resource.node_id().unwrap_or("?");
        let kernel_name = self.kernel_name.as_deref().unwrap_or("?");
        let fork = if self.is_fork { "**fork**" } else { "" };

        format!(
            "Run `{}` *{}* in *{}* kernel {}",
            node_type, node_id, kernel_name, fork,
        )
    }
}
<commit_msg>perf(Graph): Just-in-time, and once, get number of CPUs<commit_after>use std::str::FromStr;

use common::{
    eyre::{self, bail, Result},
    once_cell::sync::Lazy,
    serde::Serialize,
    strum::AsRefStr,
};
use graph_triples::ResourceInfo;

/// An execution plan for a document
#[derive(Debug, Default, Serialize)]
#[serde(crate = "common::serde")]
pub struct Plan {
    /// The options used to generate the plan
    pub options: PlanOptions,

    /// The stages to be executed
    pub stages: Vec<PlanStage>,
}

impl Plan {
    pub fn to_markdown(&self) -> String {
        let options = self.options.to_markdown();
        let stages = self
            .stages
            .iter()
            .enumerate()
            .map(|(index, stage)| format!("## Stage {}\n\n{}", index + 1, stage.to_markdown()))
            .collect::<Vec<String>>()
            .join("\n\n");
        format!("{}\n\n{}", options, stages)
    }
}

/// Options for generating a plan
#[derive(Debug, Clone, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanOptions {
    /// The ordering of nodes used when generating the plan
    pub ordering: PlanOrdering,

    /// The maximum task concurrency
    ///
    /// Limits the number of tasks that can be grouped together in a stage.
    /// Defaults to the number of logical CPU cores on the current machine.
    pub max_concurrency: usize,
}

impl PlanOptions {
    pub fn default_ordering() -> PlanOrdering {
        PlanOrdering::Topological
    }

    pub fn default_max_concurrency() -> usize {
        static CPUS: Lazy<usize> = Lazy::new(num_cpus::get);
        *CPUS
    }

    pub fn to_markdown(&self) -> String {
        format!(
            r"## Options

- Ordering: {}
- Maximum concurrency: {}",
            self.ordering.as_ref(),
            self.max_concurrency
        )
    }
}

impl Default for PlanOptions {
    fn default() -> Self {
        Self {
            ordering: Self::default_ordering(),
            max_concurrency: Self::default_max_concurrency(),
        }
    }
}

/// The ordering of nodes used when generating a plan
#[derive(Debug, Clone, Copy, Serialize, AsRefStr)]
#[serde(crate = "common::serde")]
pub enum PlanOrdering {
    /// Only a single, specified, node is to be executed
    Single,

    /// Nodes are executed in the order that they appear in the
    /// document, top to bottom, left to right.
    Appearance,

    /// Nodes are executed in the order that ensures
    /// that the dependencies of a node are executed before it is
    Topological,
}

impl FromStr for PlanOrdering {
    type Err = eyre::Report;

    fn from_str(str: &str) -> Result<PlanOrdering> {
        Ok(match str.to_lowercase().as_str() {
            "s" | "si" | "sin" | "single" => PlanOrdering::Single,
            "a" | "ap" | "app" | "appear" | "appearance" => PlanOrdering::Appearance,
            "t" | "to" | "top" | "topo" | "topological" => PlanOrdering::Topological,
            _ => bail!("Unrecognized plan ordering: {}", str),
        })
    }
}

/// The scope of a cancellation request
#[derive(Debug, Clone, Serialize, AsRefStr)]
#[serde(crate = "common::serde")]
pub enum PlanScope {
    /// A single node in the current execution plan
    Single,

    /// All nodes in the current execution plan
    All,
}

impl FromStr for PlanScope {
    type Err = eyre::Report;

    fn from_str(str: &str) -> Result<PlanScope> {
        Ok(match str.to_lowercase().as_str() {
            "s" | "si" | "sin" | "single" => PlanScope::Single,
            "a" | "all" => PlanScope::All,
            _ => bail!("Unrecognized plan scope: {}", str),
        })
    }
}

/// A stage in an execution plan
///
/// A stage represents a group of [`PlanTask`]s that can be executed concurrently
/// (e.g. because they can be executed in different kernels or a kernel fork)
#[derive(Debug, Default, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanStage {
    /// The tasks to be executed
    pub tasks: Vec<PlanTask>,
}

impl PlanStage {
    pub fn to_markdown(&self) -> String {
        self.tasks
            .iter()
            .map(|task| ["- ", &task.to_markdown()].concat())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

/// A task in an execution plan
///
/// A task is the smallest unit in an execution plan and corresponds to a kernel [`Task`]
#[derive(Debug, Serialize)]
#[serde(crate = "common::serde")]
pub struct PlanTask {
    /// Information on the resource to be executed
    ///
    /// Passed to kernel for `symbols_used` etc
    pub resource_info: ResourceInfo,

    /// The name of the kernel that the code will be executed in
    ///
    /// If this is `None` it indicates that no kernel capable of executing
    /// the node is available on the machine
    pub kernel_name: Option<String>,

    /// The code will be executed in a fork of the kernel
    ///
    /// Code that has no side effects or who's side effects should be
    /// ignored (i.e. is "@pure") are executed in a fork of the kernel.
    pub is_fork: bool,
}

impl PlanTask {
    pub fn to_markdown(&self) -> String {
        let node_type = self.resource_info.resource.node_type().unwrap_or("?");
        let node_id = self.resource_info.resource.node_id().unwrap_or("?");
        let kernel_name = self.kernel_name.as_deref().unwrap_or("?");
        let fork = if self.is_fork { "**fork**" } else { "" };

        format!(
            "Run `{}` *{}* in *{}* kernel {}",
            node_type, node_id, kernel_name, fork,
        )
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Communication channel with a physical device.
//!
//! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required
//! before you can create buffers, textures, shaders, etc.
//!
use std::ffi::CString;
use std::fmt;
use std::error;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use smallvec::SmallVec;

use instance::Features;
use instance::Instance;
use instance::PhysicalDevice;
use instance::QueueFamily;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

pub use instance::DeviceExtensions;

/// Represents a Vulkan context.
pub struct Device {
    instance: Arc<Instance>,
    physical_device: usize,
    device: vk::Device,
    vk: vk::DevicePointers,
    features: Features,
    extensions: DeviceExtensions,
}

impl Device {
    /// Builds a new Vulkan device for the given physical device.
    ///
    /// You must pass two things when creating a logical device:
    ///
    /// - A list of optional Vulkan features that must be enabled on the device. Note that if a
    ///   feature is not enabled at device creation, you can't use it later even it it's supported
    ///   by the physical device.
    ///
    /// - An iterator to a list of queues to create. Each element of the iterator must indicate
    ///   the family whose queue belongs to and a priority between 0.0 and 1.0 to assign to it.
    ///   A queue with a higher value indicates that the commands will execute faster than on a
    ///   queue with a lower value. Note however that no guarantee can be made on the way the
    ///   priority value is handled by the implementation.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the requested features is not supported by the physical device.
    /// - Panicks if one of the queue families doesn't belong to the given device.
    /// - Panicks if you request more queues from a family than available.
    /// - Panicks if one of the priorities is outside of the `[0.0 ; 1.0]` range.
    ///
    // TODO: return Arc<Queue> and handle synchronization in the Queue
    pub fn new<'a, I, L>(phys: &'a PhysicalDevice, requested_features: &Features,
                         extensions: &DeviceExtensions, layers: L, queue_families: I)
                         -> Result<(Arc<Device>, Vec<Arc<Queue>>), DeviceCreationError>
        where I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
              L: IntoIterator<Item = &'a &'a str>
    {
        let queue_families = queue_families.into_iter();

        assert!(phys.supported_features().superset_of(&requested_features));

        let vk_i = phys.instance().pointers();

        // this variable will contain the queue family ID and queue ID of each requested queue
        let mut output_queues: Vec<(u32, u32)> = Vec::with_capacity(queue_families.size_hint().0);

        let layers = layers.into_iter().map(|&layer| {
            // FIXME: check whether each layer is supported
            CString::new(layer).unwrap()
        }).collect::<SmallVec<[_; 8]>>();
        let layers = layers.iter().map(|layer| {
            layer.as_ptr()
        }).collect::<SmallVec<[_; 8]>>();

        let extensions_list = extensions.build_extensions_list();
        let extensions_list = extensions_list.iter().map(|extension| {
            extension.as_ptr()
        }).collect::<SmallVec<[_; 16]>>();

        // device creation
        let device = unsafe {
            // each element of `queues` is a `(queue_family, priorities)`
            // each queue family must only have one entry in `queues`
            let mut queues: Vec<(u32, Vec<f32>)> = Vec::with_capacity(phys.queue_families().len());

            for (queue_family, priority) in queue_families {
                // checking the parameters
                assert_eq!(queue_family.physical_device().internal_object(),
                           phys.internal_object());
                assert!(priority >= 0.0 && priority <= 1.0);

                // adding to `queues` and `output_queues`
                if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) {
                    output_queues.push((queue_family.id(), q.1.len() as u32));
                    q.1.push(priority);
                    assert!(q.1.len() < queue_family.queues_count());
                    continue;
                }
                queues.push((queue_family.id(), vec![priority]));
                output_queues.push((queue_family.id(), 0));
            }

            // turning `queues` into an array of `vkDeviceQueueCreateInfo` suitable for Vulkan
            let queues = queues.iter().map(|&(queue_id, ref priorities)| {
                vk::DeviceQueueCreateInfo {
                    sType: vk::STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                    pNext: ptr::null(),
                    flags: 0,   // reserved
                    queueFamilyIndex: queue_id,
                    queueCount: priorities.len() as u32,
                    pQueuePriorities: priorities.as_ptr()
                }
            }).collect::<SmallVec<[_; 16]>>();

            let features: vk::PhysicalDeviceFeatures = requested_features.clone().into();

            let infos = vk::DeviceCreateInfo {
                sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                queueCreateInfoCount: queues.len() as u32,
                pQueueCreateInfos: queues.as_ptr(),
                enabledLayerCount: layers.len() as u32,
                ppEnabledLayerNames: layers.as_ptr(),
                enabledExtensionCount: extensions_list.len() as u32,
                ppEnabledExtensionNames: extensions_list.as_ptr(),
                pEnabledFeatures: &features,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk_i.CreateDevice(phys.internal_object(), &infos,
                                                ptr::null(), &mut output)));
            output
        };

        // loading the function pointers of the newly-created device
        let vk = vk::DevicePointers::load(|name| {
            unsafe { vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _ }
        });

        let device = Arc::new(Device {
            instance: phys.instance().clone(),
            physical_device: phys.index(),
            device: device,
            vk: vk,
            features: requested_features.clone(),
            extensions: extensions.clone(),
        });

        // querying the queues
        let output_queues = output_queues.into_iter().map(|(family, id)| {
            unsafe {
                let mut output = mem::uninitialized();
                device.vk.GetDeviceQueue(device.device, family, id, &mut output);
                Arc::new(Queue {
                    queue: Mutex::new(output),
                    device: device.clone(),
                    family: family,
                    id: id,
                    dedicated_semaphore: Mutex::new(None),
                })
            }
        }).collect();

        Ok((device, output_queues))
    }

    /// Waits until all work on this device has finished. You should never need to call
    /// this function, but it can be useful for debugging or benchmarking purposes.
    ///
    /// This is the Vulkan equivalent of `glFinish`.
    // FIXME: must synchronize all queuees
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
            Ok(())
        }
    }

    /// Returns the instance used to create this device.
    #[inline]
    pub fn instance(&self) -> &Arc<Instance> {
        &self.instance
    }

    /// Returns the physical device that was used to create this device.
    #[inline]
    pub fn physical_device(&self) -> PhysicalDevice {
        PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap()
    }

    /// Returns the features that are enabled in the device.
    #[inline]
    pub fn enabled_features(&self) -> &Features {
        &self.features
    }

    /// Returns the list of extensions that have been loaded.
    #[inline]
    pub fn loaded_extensions(&self) -> &DeviceExtensions {
        &self.extensions
    }
}

impl fmt::Debug for Device {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "<Vulkan device>")
    }
}

unsafe impl VulkanObject for Device {
    type Object = vk::Device;

    #[inline]
    fn internal_object(&self) -> vk::Device {
        self.device
    }
}

impl VulkanPointers for Device {
    type Pointers = vk::DevicePointers;

    #[inline]
    fn pointers(&self) -> &vk::DevicePointers {
        &self.vk
    }
}

impl Drop for Device {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            self.vk.DeviceWaitIdle(self.device);
            self.vk.DestroyDevice(self.device, ptr::null());
        }
    }
}

/// Error that can be returned when creating a device.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceCreationError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
    // FIXME: other values
}

impl error::Error for DeviceCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            DeviceCreationError::OutOfHostMemory => "no memory available on the host",
            DeviceCreationError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for DeviceCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for DeviceCreationError {
    #[inline]
    fn from(err: Error) -> DeviceCreationError {
        match err {
            Error::OutOfHostMemory => DeviceCreationError::OutOfHostMemory,
            Error::OutOfDeviceMemory => DeviceCreationError::OutOfDeviceMemory,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

/// Represents a queue where commands can be submitted.
// TODO: should use internal synchronization
#[derive(Debug)]
pub struct Queue {
    queue: Mutex<vk::Queue>,
    device: Arc<Device>,
    family: u32,
    id: u32,    // id within family

    // For safety purposes, each command buffer submitted to a queue has to both wait on and
    // signal the semaphore specified here.
    //
    // If this is `None`, then that means we haven't used the semaphore yet.
    //
    // For more infos, see TODO: see what?
    dedicated_semaphore: Mutex<Option<Arc<Semaphore>>>,
}

impl Queue {
    /// Returns the device this queue belongs to.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the family this queue belongs to.
    #[inline]
    pub fn family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.family).unwrap()
    }

    /// Returns the index of this queue within its family.
    #[inline]
    pub fn id_within_family(&self) -> u32 {
        self.id
    }

    /// Waits until all work on this queue has finished.
    ///
    /// Just like `Device::wait()`, you shouldn't have to call this function.
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let queue = self.queue.lock().unwrap();
            try!(check_errors(vk.QueueWaitIdle(*queue)));
            Ok(())
        }
    }

    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    /*// TODO: document
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self) -> Result<(Arc<Semaphore>, bool), OomError> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();

        if let Some(ref semaphore) = *sem {
            return Ok((semaphore.clone(), true));
        }

        let semaphore = try!(Semaphore::new(&self.device));
        *sem = Some(semaphore.clone());
        Ok((semaphore, false))
    }*/
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self, signalled: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();
        mem::replace(&mut *sem, Some(signalled))
    }
}

unsafe impl SynchronizedVulkanObject for Queue {
    type Object = vk::Queue;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Queue> {
        self.queue.lock().unwrap()
    }
}
<commit_msg>Always enable robustBufferAccess<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Communication channel with a physical device.
//!
//! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required
//! before you can create buffers, textures, shaders, etc.
//!
use std::ffi::CString;
use std::fmt;
use std::error;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use smallvec::SmallVec;

use instance::Features;
use instance::Instance;
use instance::PhysicalDevice;
use instance::QueueFamily;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

pub use instance::DeviceExtensions;

/// Represents a Vulkan context.
pub struct Device {
    instance: Arc<Instance>,
    physical_device: usize,
    device: vk::Device,
    vk: vk::DevicePointers,
    features: Features,
    extensions: DeviceExtensions,
}

impl Device {
    /// Builds a new Vulkan device for the given physical device.
    ///
    /// You must pass two things when creating a logical device:
    ///
    /// - A list of optional Vulkan features that must be enabled on the device. Note that if a
    ///   feature is not enabled at device creation, you can't use it later even it it's supported
    ///   by the physical device.
    ///
    /// - An iterator to a list of queues to create. Each element of the iterator must indicate
    ///   the family whose queue belongs to and a priority between 0.0 and 1.0 to assign to it.
    ///   A queue with a higher value indicates that the commands will execute faster than on a
    ///   queue with a lower value. Note however that no guarantee can be made on the way the
    ///   priority value is handled by the implementation.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the requested features is not supported by the physical device.
    /// - Panicks if one of the queue families doesn't belong to the given device.
    /// - Panicks if you request more queues from a family than available.
    /// - Panicks if one of the priorities is outside of the `[0.0 ; 1.0]` range.
    ///
    // TODO: return Arc<Queue> and handle synchronization in the Queue
    pub fn new<'a, I, L>(phys: &'a PhysicalDevice, requested_features: &Features,
                         extensions: &DeviceExtensions, layers: L, queue_families: I)
                         -> Result<(Arc<Device>, Vec<Arc<Queue>>), DeviceCreationError>
        where I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
              L: IntoIterator<Item = &'a &'a str>
    {
        let queue_families = queue_families.into_iter();

        assert!(phys.supported_features().superset_of(&requested_features));

        let vk_i = phys.instance().pointers();

        // this variable will contain the queue family ID and queue ID of each requested queue
        let mut output_queues: Vec<(u32, u32)> = Vec::with_capacity(queue_families.size_hint().0);

        let layers = layers.into_iter().map(|&layer| {
            // FIXME: check whether each layer is supported
            CString::new(layer).unwrap()
        }).collect::<SmallVec<[_; 8]>>();
        let layers = layers.iter().map(|layer| {
            layer.as_ptr()
        }).collect::<SmallVec<[_; 8]>>();

        let extensions_list = extensions.build_extensions_list();
        let extensions_list = extensions_list.iter().map(|extension| {
            extension.as_ptr()
        }).collect::<SmallVec<[_; 16]>>();

        // device creation
        let device = unsafe {
            // each element of `queues` is a `(queue_family, priorities)`
            // each queue family must only have one entry in `queues`
            let mut queues: Vec<(u32, Vec<f32>)> = Vec::with_capacity(phys.queue_families().len());

            for (queue_family, priority) in queue_families {
                // checking the parameters
                assert_eq!(queue_family.physical_device().internal_object(),
                           phys.internal_object());
                assert!(priority >= 0.0 && priority <= 1.0);

                // adding to `queues` and `output_queues`
                if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) {
                    output_queues.push((queue_family.id(), q.1.len() as u32));
                    q.1.push(priority);
                    assert!(q.1.len() < queue_family.queues_count());
                    continue;
                }
                queues.push((queue_family.id(), vec![priority]));
                output_queues.push((queue_family.id(), 0));
            }

            // turning `queues` into an array of `vkDeviceQueueCreateInfo` suitable for Vulkan
            let queues = queues.iter().map(|&(queue_id, ref priorities)| {
                vk::DeviceQueueCreateInfo {
                    sType: vk::STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                    pNext: ptr::null(),
                    flags: 0,   // reserved
                    queueFamilyIndex: queue_id,
                    queueCount: priorities.len() as u32,
                    pQueuePriorities: priorities.as_ptr()
                }
            }).collect::<SmallVec<[_; 16]>>();

            // TODO: The plan regarding `robustBufferAccess` is to check the shaders' code to see
            //       if they can possibly perform out-of-bounds reads and writes. If the user tries
            //       to use a shader that can perform out-of-bounds operations without having
            //       `robustBufferAccess` enabled, an error is returned.
            //
            //       However for the moment this verification isn't performed. In order to be safe,
            //       we always enable the `robustBufferAccess` feature as it is guaranteed to be
            //       supported everywhere.
            //
            //       The only alternative (while waiting for shaders introspection to work) is to
            //       make all shaders depend on `robustBufferAccess`. But since usually the
            //       majority of shaders don't need this feature, it would be very annoying to have
            //       to enable it manually when you don't need it.
            let features = {
                let mut features: vk::PhysicalDeviceFeatures = requested_features.clone().into();
                features.robustBufferAccess = vk::TRUE;
                features
            };

            let infos = vk::DeviceCreateInfo {
                sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                queueCreateInfoCount: queues.len() as u32,
                pQueueCreateInfos: queues.as_ptr(),
                enabledLayerCount: layers.len() as u32,
                ppEnabledLayerNames: layers.as_ptr(),
                enabledExtensionCount: extensions_list.len() as u32,
                ppEnabledExtensionNames: extensions_list.as_ptr(),
                pEnabledFeatures: &features,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk_i.CreateDevice(phys.internal_object(), &infos,
                                                ptr::null(), &mut output)));
            output
        };

        // loading the function pointers of the newly-created device
        let vk = vk::DevicePointers::load(|name| {
            unsafe { vk_i.GetDeviceProcAddr(device, name.as_ptr()) as *const _ }
        });

        let device = Arc::new(Device {
            instance: phys.instance().clone(),
            physical_device: phys.index(),
            device: device,
            vk: vk,
            features: requested_features.clone(),
            extensions: extensions.clone(),
        });

        // querying the queues
        let output_queues = output_queues.into_iter().map(|(family, id)| {
            unsafe {
                let mut output = mem::uninitialized();
                device.vk.GetDeviceQueue(device.device, family, id, &mut output);
                Arc::new(Queue {
                    queue: Mutex::new(output),
                    device: device.clone(),
                    family: family,
                    id: id,
                    dedicated_semaphore: Mutex::new(None),
                })
            }
        }).collect();

        Ok((device, output_queues))
    }

    /// Waits until all work on this device has finished. You should never need to call
    /// this function, but it can be useful for debugging or benchmarking purposes.
    ///
    /// This is the Vulkan equivalent of `glFinish`.
    // FIXME: must synchronize all queuees
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            try!(check_errors(self.vk.DeviceWaitIdle(self.device)));
            Ok(())
        }
    }

    /// Returns the instance used to create this device.
    #[inline]
    pub fn instance(&self) -> &Arc<Instance> {
        &self.instance
    }

    /// Returns the physical device that was used to create this device.
    #[inline]
    pub fn physical_device(&self) -> PhysicalDevice {
        PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap()
    }

    /// Returns the features that are enabled in the device.
    #[inline]
    pub fn enabled_features(&self) -> &Features {
        &self.features
    }

    /// Returns the list of extensions that have been loaded.
    #[inline]
    pub fn loaded_extensions(&self) -> &DeviceExtensions {
        &self.extensions
    }
}

impl fmt::Debug for Device {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "<Vulkan device>")
    }
}

unsafe impl VulkanObject for Device {
    type Object = vk::Device;

    #[inline]
    fn internal_object(&self) -> vk::Device {
        self.device
    }
}

impl VulkanPointers for Device {
    type Pointers = vk::DevicePointers;

    #[inline]
    fn pointers(&self) -> &vk::DevicePointers {
        &self.vk
    }
}

impl Drop for Device {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            self.vk.DeviceWaitIdle(self.device);
            self.vk.DestroyDevice(self.device, ptr::null());
        }
    }
}

/// Error that can be returned when creating a device.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DeviceCreationError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
    // FIXME: other values
}

impl error::Error for DeviceCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            DeviceCreationError::OutOfHostMemory => "no memory available on the host",
            DeviceCreationError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for DeviceCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for DeviceCreationError {
    #[inline]
    fn from(err: Error) -> DeviceCreationError {
        match err {
            Error::OutOfHostMemory => DeviceCreationError::OutOfHostMemory,
            Error::OutOfDeviceMemory => DeviceCreationError::OutOfDeviceMemory,
            _ => panic!("Unexpected error value: {}", err as i32)
        }
    }
}

/// Represents a queue where commands can be submitted.
// TODO: should use internal synchronization
#[derive(Debug)]
pub struct Queue {
    queue: Mutex<vk::Queue>,
    device: Arc<Device>,
    family: u32,
    id: u32,    // id within family

    // For safety purposes, each command buffer submitted to a queue has to both wait on and
    // signal the semaphore specified here.
    //
    // If this is `None`, then that means we haven't used the semaphore yet.
    //
    // For more infos, see TODO: see what?
    dedicated_semaphore: Mutex<Option<Arc<Semaphore>>>,
}

impl Queue {
    /// Returns the device this queue belongs to.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the family this queue belongs to.
    #[inline]
    pub fn family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.family).unwrap()
    }

    /// Returns the index of this queue within its family.
    #[inline]
    pub fn id_within_family(&self) -> u32 {
        self.id
    }

    /// Waits until all work on this queue has finished.
    ///
    /// Just like `Device::wait()`, you shouldn't have to call this function.
    #[inline]
    pub fn wait(&self) -> Result<(), OomError> {
        unsafe {
            let vk = self.device.pointers();
            let queue = self.queue.lock().unwrap();
            try!(check_errors(vk.QueueWaitIdle(*queue)));
            Ok(())
        }
    }

    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    /*// TODO: document
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self) -> Result<(Arc<Semaphore>, bool), OomError> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();

        if let Some(ref semaphore) = *sem {
            return Ok((semaphore.clone(), true));
        }

        let semaphore = try!(Semaphore::new(&self.device));
        *sem = Some(semaphore.clone());
        Ok((semaphore, false))
    }*/
    #[doc(hidden)]
    #[inline]
    pub unsafe fn dedicated_semaphore(&self, signalled: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut sem = self.dedicated_semaphore.lock().unwrap();
        mem::replace(&mut *sem, Some(signalled))
    }
}

unsafe impl SynchronizedVulkanObject for Queue {
    type Object = vk::Queue;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::Queue> {
        self.queue.lock().unwrap()
    }
}
<|endoftext|>


<commit_before>extern crate mws;
use chrono::offset::TimeZone;
use chrono::{DateTime, NaiveDate, Utc};
use std::path::PathBuf;
use structopt::StructOpt;

use mws::client::{Client, Method};

mod env;

use self::env::Env;

#[derive(Debug, StructOpt)]
#[structopt(name = "mws-cli", about = "MWS CLI.")]
struct Opt {
  #[structopt(long = "env", parse(from_os_str))]
  env: Option<PathBuf>,
  #[structopt(subcommand)]
  cmd: Command,
}

#[derive(Debug, StructOpt)]
enum Command {
  ReportRequest {
    #[structopt(long = "report_type")]
    report_type: String,
    #[structopt(long = "start_date", parse(try_from_str))]
    start_date: Option<NaiveDate>,
    #[structopt(long = "end_date", parse(try_from_str))]
    end_date: Option<NaiveDate>,
    #[structopt(long = "report_options")]
    report_options: Option<String>,
    #[structopt(long = "marketplace")]
    marketplace_id_list: Option<Vec<String>>,
  },
  ReportListRequestByIds {
    #[structopt(long = "id")]
    ids: Vec<String>,
  },
  ReportListRequestByTypes {
    #[structopt(long = "type")]
    types: Vec<String>,
  },
  ReportGet {
    #[structopt(long = "id")]
    id: String,
    #[structopt(long = "out", parse(from_os_str))]
    out: PathBuf,
  },
  EncodingConvJp {
    #[structopt(long = "in", parse(from_os_str))]
    input: PathBuf,
    #[structopt(long = "out", parse(from_os_str))]
    out: PathBuf,
  },
  ProductGetLowestPricedOffersForSKU {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
    #[structopt(long = "seller_sku")]
    seller_sku: String,
    #[structopt(long = "condition")]
    condition: String,
  },
  ProductGetMyPriceForASIN {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
    #[structopt(long = "asin")]
    asins: Vec<String>,
    #[structopt(long = "condition")]
    condition: Option<String>,
  },
  SubmitFeed {
    #[structopt(long = "feed_type")]
    feed_type: String,
    #[structopt(long = "contant_file", parse(from_os_str))]
    content_file: PathBuf,
    #[structopt(long = "marketplace")]
    marketplace_id_list: Option<Vec<String>>,
    #[structopt(long = "content_type")]
    content_type: String,
  },
  ListSubscriptions {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
  },
  ListFinancialEvents {
    #[structopt(long = "posted_after", parse(try_from_str))]
    posted_after: NaiveDate,
    #[structopt(long = "posted_before", parse(try_from_str))]
    posted_before: NaiveDate,
    #[structopt(long = "out", parse(from_os_str))]
    outdir: PathBuf,
  },
}

fn main() {
  let opt = Opt::from_args();

  let env_path = opt.env.unwrap_or_else(|| PathBuf::from(".env"));

  println!("env path: {:?}", env_path);
  ::dotenv::from_path(&env_path).ok();

  let env = Env::from_env();

  println!("seller id: {}", env.seller_id);
  println!("region id: {}", env.region_id);

  let client = get_client(&env);

  match opt.cmd {
    Command::ReportRequest {
      report_type,
      start_date,
      end_date,
      report_options,
      marketplace_id_list,
    } => {
      use mws::reports::*;
      let res = RequestReport(
        &client,
        RequestReportParameters {
          ReportType: report_type,
          StartDate: start_date.map(get_utc_datetime),
          EndDate: end_date.map(get_utc_datetime),
          ReportOptions: report_options,
          MarketplaceIdList: marketplace_id_list,
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ReportListRequestByIds { ids } => {
      use mws::reports::*;
      let res = GetReportRequestList(
        &client,
        GetReportRequestListParameters {
          ReportRequestIdList: Some(ids),
          ..Default::default()
        },
      )
      .unwrap();

      println!("{:#?}", res)
    }
    Command::ReportListRequestByTypes { types } => {
      use mws::reports::*;
      let res = GetReportRequestList(
        &client,
        GetReportRequestListParameters {
          ReportTypeList: Some(types),
          ..Default::default()
        },
      )
      .unwrap();

      println!("{:#?}", res)
    }
    Command::ReportGet { id, out } => {
      use mws::reports::*;
      let mut out = std::fs::File::create(out).unwrap();
      GetReport(&client, id, &mut out).unwrap();
    }
    Command::EncodingConvJp { input, out } => {
      use encoding_rs::*;
      let bytes = std::fs::read(input).unwrap();
      let (cow, encoding_used, had_errors) = SHIFT_JIS.decode(&bytes);
      if had_errors {
        panic!("decode error.")
      }
      println!("encoding_used: {:?}", encoding_used);
      std::fs::write(out, cow.as_ref()).unwrap();
    }
    Command::ProductGetLowestPricedOffersForSKU {
      marketplace_id,
      seller_sku,
      condition,
    } => {
      use mws::products::*;
      let res = GetLowestPricedOffersForSKU(
        &client,
        GetLowestPricedOffersForSKUParameters {
          MarketplaceId: marketplace_id,
          SellerSKU: seller_sku,
          ItemCondition: ItemCondition::from(&condition as &str),
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ProductGetMyPriceForASIN {
      marketplace_id,
      asins,
      condition,
    } => {
      use mws::products::*;
      let res = GetMyPriceForASIN(
        &client,
        GetMyPriceForASINParameters {
          MarketplaceId: marketplace_id,
          ASINList: asins,
          ItemCondition: condition.map(|v| ItemCondition::from(&v as &str)),
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::SubmitFeed {
      feed_type,
      content_file,
      marketplace_id_list,
      content_type,
    } => {
      use mws::feeds::*;
      use std::io::Cursor;
      let content = std::fs::read(content_file).unwrap();
      let digest = md5::compute(&content);
      let b64 = base64::encode(&*digest);
      let res = SubmitFeed(
        &client,
        SubmitFeedParameters {
          FeedType: feed_type,
          MarketplaceIdList: marketplace_id_list,
          PurgeAndReplace: None,
        },
        Cursor::new(content),
        b64,
        content_type,
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ListSubscriptions { marketplace_id } => {
      use mws::subscriptions::*;
      let res = ListSubscriptions(&client, marketplace_id).unwrap();
      println!("{:#?}", res)
    }
    Command::ListFinancialEvents {
      posted_after,
      posted_before,
      outdir,
    } => {
      use std::thread::sleep;
      use std::time::Duration;
      let mut page = 1;
      let mut next_token: Option<String> = None;
      loop {
        println!("loading page {} ...", page);

        let res = if let Some(next_token) = next_token.take() {
          client
            .request_xml_generic(
              Method::Post,
              "/Finances",
              "2015-05-01",
              "ListFinancialEventsByNextToken",
              vec![
                (
                  "NextToken".to_string(),
                  next_token,
                ),
              ],
            )
            .unwrap()
        } else {
          client
            .request_xml_generic(
              Method::Post,
              "/Finances",
              "2015-05-01",
              "ListFinancialEvents",
              vec![
                (
                  "PostedAfter".to_string(),
                  get_utc_datetime(posted_after).to_rfc3339(),
                ),
                (
                  "PostedBefore".to_string(),
                  get_utc_datetime(posted_before).to_rfc3339(),
                ),
              ],
            )
            .unwrap()
        };

        let filename = format!("financial_events_{}_{}_{}.xml", posted_after, posted_before, page);
        let f = std::fs::File::create(outdir.join(filename)).unwrap();
        res.result_element.write(f).unwrap();

        next_token = res.next_token().clone().map(|v| v.to_string());
        if next_token.is_none() {
          break;
        }

        page += 1;

        sleep(Duration::from_secs(1));
      }
    }
  }
}

fn get_client(env: &Env) -> Client {
  use mws::client::ClientOptions;
  use mws::constants;
  let region = constants::get_region(&env.region_id).expect("invalid region id");
  let opts = ClientOptions {
    endpoint: region.endpoint.to_string(),
    seller_id: env.seller_id.clone(),
    mws_auth_token: env.auth_token.clone(),
    aws_access_key_id: env.access_key_id.clone(),
    secret_key: env.secret_key.clone(),
  };
  Client::new(opts).unwrap()
}

fn get_utc_datetime(date: NaiveDate) -> DateTime<Utc> {
  Utc.from_utc_date(&date).and_hms(0, 0, 0)
}
<commit_msg>feat: financing event chunks<commit_after>extern crate mws;
use chrono::offset::TimeZone;
use chrono::{DateTime, NaiveDate, Utc};
use std::path::PathBuf;
use structopt::StructOpt;

use mws::client::{Client, Method};

mod env;

use self::env::Env;

#[derive(Debug, StructOpt)]
#[structopt(name = "mws-cli", about = "MWS CLI.")]
struct Opt {
  #[structopt(long = "env", parse(from_os_str))]
  env: Option<PathBuf>,
  #[structopt(subcommand)]
  cmd: Command,
}

#[derive(Debug, StructOpt)]
enum Command {
  ReportRequest {
    #[structopt(long = "report_type")]
    report_type: String,
    #[structopt(long = "start_date", parse(try_from_str))]
    start_date: Option<NaiveDate>,
    #[structopt(long = "end_date", parse(try_from_str))]
    end_date: Option<NaiveDate>,
    #[structopt(long = "report_options")]
    report_options: Option<String>,
    #[structopt(long = "marketplace")]
    marketplace_id_list: Option<Vec<String>>,
  },
  ReportListRequestByIds {
    #[structopt(long = "id")]
    ids: Vec<String>,
  },
  ReportListRequestByTypes {
    #[structopt(long = "type")]
    types: Vec<String>,
  },
  ReportGet {
    #[structopt(long = "id")]
    id: String,
    #[structopt(long = "out", parse(from_os_str))]
    out: PathBuf,
  },
  EncodingConvJp {
    #[structopt(long = "in", parse(from_os_str))]
    input: PathBuf,
    #[structopt(long = "out", parse(from_os_str))]
    out: PathBuf,
  },
  ProductGetLowestPricedOffersForSKU {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
    #[structopt(long = "seller_sku")]
    seller_sku: String,
    #[structopt(long = "condition")]
    condition: String,
  },
  ProductGetMyPriceForASIN {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
    #[structopt(long = "asin")]
    asins: Vec<String>,
    #[structopt(long = "condition")]
    condition: Option<String>,
  },
  SubmitFeed {
    #[structopt(long = "feed_type")]
    feed_type: String,
    #[structopt(long = "contant_file", parse(from_os_str))]
    content_file: PathBuf,
    #[structopt(long = "marketplace")]
    marketplace_id_list: Option<Vec<String>>,
    #[structopt(long = "content_type")]
    content_type: String,
  },
  ListSubscriptions {
    #[structopt(long = "marketplace")]
    marketplace_id: String,
  },
  ListFinancialEvents {
    #[structopt(long = "posted_after", parse(try_from_str))]
    posted_after: NaiveDate,
    #[structopt(long = "posted_before", parse(try_from_str))]
    posted_before: NaiveDate,
    #[structopt(long = "out", parse(from_os_str))]
    outdir: PathBuf,
  },
}

fn main() {
  let opt = Opt::from_args();

  let env_path = opt.env.unwrap_or_else(|| PathBuf::from(".env"));

  println!("env path: {:?}", env_path);
  ::dotenv::from_path(&env_path).ok();

  let env = Env::from_env();

  println!("seller id: {}", env.seller_id);
  println!("region id: {}", env.region_id);

  let client = get_client(&env);

  match opt.cmd {
    Command::ReportRequest {
      report_type,
      start_date,
      end_date,
      report_options,
      marketplace_id_list,
    } => {
      use mws::reports::*;
      let res = RequestReport(
        &client,
        RequestReportParameters {
          ReportType: report_type,
          StartDate: start_date.map(get_utc_datetime),
          EndDate: end_date.map(get_utc_datetime),
          ReportOptions: report_options,
          MarketplaceIdList: marketplace_id_list,
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ReportListRequestByIds { ids } => {
      use mws::reports::*;
      let res = GetReportRequestList(
        &client,
        GetReportRequestListParameters {
          ReportRequestIdList: Some(ids),
          ..Default::default()
        },
      )
      .unwrap();

      println!("{:#?}", res)
    }
    Command::ReportListRequestByTypes { types } => {
      use mws::reports::*;
      let res = GetReportRequestList(
        &client,
        GetReportRequestListParameters {
          ReportTypeList: Some(types),
          ..Default::default()
        },
      )
      .unwrap();

      println!("{:#?}", res)
    }
    Command::ReportGet { id, out } => {
      use mws::reports::*;
      let mut out = std::fs::File::create(out).unwrap();
      GetReport(&client, id, &mut out).unwrap();
    }
    Command::EncodingConvJp { input, out } => {
      use encoding_rs::*;
      let bytes = std::fs::read(input).unwrap();
      let (cow, encoding_used, had_errors) = SHIFT_JIS.decode(&bytes);
      if had_errors {
        panic!("decode error.")
      }
      println!("encoding_used: {:?}", encoding_used);
      std::fs::write(out, cow.as_ref()).unwrap();
    }
    Command::ProductGetLowestPricedOffersForSKU {
      marketplace_id,
      seller_sku,
      condition,
    } => {
      use mws::products::*;
      let res = GetLowestPricedOffersForSKU(
        &client,
        GetLowestPricedOffersForSKUParameters {
          MarketplaceId: marketplace_id,
          SellerSKU: seller_sku,
          ItemCondition: ItemCondition::from(&condition as &str),
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ProductGetMyPriceForASIN {
      marketplace_id,
      asins,
      condition,
    } => {
      use mws::products::*;
      let res = GetMyPriceForASIN(
        &client,
        GetMyPriceForASINParameters {
          MarketplaceId: marketplace_id,
          ASINList: asins,
          ItemCondition: condition.map(|v| ItemCondition::from(&v as &str)),
        },
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::SubmitFeed {
      feed_type,
      content_file,
      marketplace_id_list,
      content_type,
    } => {
      use mws::feeds::*;
      use std::io::Cursor;
      let content = std::fs::read(content_file).unwrap();
      let digest = md5::compute(&content);
      let b64 = base64::encode(&*digest);
      let res = SubmitFeed(
        &client,
        SubmitFeedParameters {
          FeedType: feed_type,
          MarketplaceIdList: marketplace_id_list,
          PurgeAndReplace: None,
        },
        Cursor::new(content),
        b64,
        content_type,
      )
      .unwrap();
      println!("{:#?}", res)
    }
    Command::ListSubscriptions { marketplace_id } => {
      use mws::subscriptions::*;
      let res = ListSubscriptions(&client, marketplace_id).unwrap();
      println!("{:#?}", res)
    }
    Command::ListFinancialEvents {
      posted_after,
      posted_before,
      outdir,
    } => {
      use std::thread::sleep;
      use std::time::Duration;
      let mut page = 1;
      let mut next_token: Option<String> = None;

      let duration = posted_before - posted_after;
      let days = duration.num_days();

      if days < 0 {
        panic!("invalid date range")
      }

      let chunks = ((days as f64) / 180.0).ceil() as i64;
      let ranges: Vec<[NaiveDate; 2]> = (0..chunks).into_iter().map(|chunk_idx| {
        [posted_after + chrono::Duration::days(chunk_idx * 180), std::cmp::min(posted_before, posted_after + chrono::Duration::days((chunk_idx + 1) * 180))]
      }).collect();

      println!("ranges = {:?}", ranges);

      for [posted_after, posted_before] in ranges {
        loop {
          println!("loading page {} ...", page);

          let res = if let Some(next_token) = next_token.take() {
            client
              .request_xml_generic(
                Method::Post,
                "/Finances",
                "2015-05-01",
                "ListFinancialEventsByNextToken",
                vec![
                  (
                    "NextToken".to_string(),
                    next_token,
                  ),
                ],
              )
              .unwrap()
          } else {
            client
              .request_xml_generic(
                Method::Post,
                "/Finances",
                "2015-05-01",
                "ListFinancialEvents",
                vec![
                  (
                    "PostedAfter".to_string(),
                    get_utc_datetime(posted_after).to_rfc3339(),
                  ),
                  (
                    "PostedBefore".to_string(),
                    get_utc_datetime(posted_before).to_rfc3339(),
                  ),
                ],
              )
              .unwrap()
          };

          let filename = format!("financial_events_{}_{}_{}.xml", posted_after, posted_before, page);
          let f = std::fs::File::create(outdir.join(filename)).unwrap();
          res.result_element.write(f).unwrap();

          next_token = res.next_token().clone().map(|v| v.to_string());
          if next_token.is_none() {
            break;
          }

          page += 1;

          sleep(Duration::from_secs(1));
        }
      }
    }
  }
}

fn get_client(env: &Env) -> Client {
  use mws::client::ClientOptions;
  use mws::constants;
  let region = constants::get_region(&env.region_id).expect("invalid region id");
  let opts = ClientOptions {
    endpoint: region.endpoint.to_string(),
    seller_id: env.seller_id.clone(),
    mws_auth_token: env.auth_token.clone(),
    aws_access_key_id: env.access_key_id.clone(),
    secret_key: env.secret_key.clone(),
  };
  Client::new(opts).unwrap()
}

fn get_utc_datetime(date: NaiveDate) -> DateTime<Utc> {
  Utc.from_utc_date(&date).and_hms(0, 0, 0)
}
<|endoftext|>