<commit_before>use crate::config::Config;
use crate::err::Result;
use crate::file::{FileType, SaveDir, SaveFile};
use crate::series::remote::{RemoteService, SeriesEntry, SeriesInfo, Status};
use chrono::{Local, NaiveDate};
use serde_derive::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
pub struct EntryState {
    entry: SeriesEntry,
    needs_sync: bool,
}

impl EntryState {
    pub fn new(entry: SeriesEntry) -> EntryState {
        EntryState {
            entry,
            needs_sync: false,
        }
    }

    pub fn sync_changes_to_remote<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        if !self.needs_sync {
            return Ok(());
        }

        let remote = remote.as_ref();

        if remote.is_offline() {
            self.save(name.as_ref())?;
            return Ok(());
        }

        remote.update_list_entry(&self.entry)?;

        self.needs_sync = false;
        self.save(name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_from_remote<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        let remote = remote.as_ref();

        if self.needs_sync || remote.is_offline() {
            return Ok(());
        }

        let entry = match remote.get_list_entry(self.entry.id)? {
            Some(entry) => entry,
            None => return Ok(()),
        };

        self.entry = entry;
        self.save(name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        self.sync_changes_from_remote(&remote, &name)?;
        self.sync_changes_to_remote(&remote, &name)
    }

    #[inline(always)]
    pub fn watched_eps(&self) -> u32 {
        self.entry.watched_eps
    }

    #[inline(always)]
    pub fn set_watched_eps(&mut self, watched_eps: u32) {
        self.entry.watched_eps = watched_eps;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn score(&self) -> Option<f32> {
        self.entry.score
    }

    #[inline(always)]
    pub fn status(&self) -> Status {
        self.entry.status
    }

    #[inline(always)]
    pub fn set_status(&mut self, status: Status) {
        self.entry.status = status;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn times_rewatched(&self) -> u32 {
        self.entry.times_rewatched
    }

    #[inline(always)]
    pub fn set_times_rewatched(&mut self, times_rewatched: u32) {
        self.entry.times_rewatched = times_rewatched;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn start_date(&self) -> Option<NaiveDate> {
        self.entry.start_date
    }

    #[inline(always)]
    pub fn set_start_date(&mut self, start_date: Option<NaiveDate>) {
        self.entry.start_date = start_date;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn end_date(&self) -> Option<NaiveDate> {
        self.entry.end_date
    }

    #[inline(always)]
    pub fn set_end_date(&mut self, end_date: Option<NaiveDate>) {
        self.entry.end_date = end_date;
        self.needs_sync = true;
    }
}

impl SaveFile for EntryState {
    fn filename() -> &'static str {
        "entry_state.mpack"
    }

    fn save_dir() -> SaveDir {
        SaveDir::LocalData
    }

    fn file_type() -> FileType {
        FileType::MessagePack
    }
}

#[derive(Debug)]
pub struct SeriesTracker<'a> {
    pub info: &'a SeriesInfo,
    pub state: EntryState,
    pub name: String,
}

impl<'a> SeriesTracker<'a> {
    pub fn init<R, S>(remote: R, info: &'a SeriesInfo, name: S) -> Result<SeriesTracker<'a>>
    where
        R: AsRef<RemoteService>,
        S: Into<String>,
    {
        let name = name.into();

        let mut state = match EntryState::load(name.as_ref()) {
            Ok(state) => state,
            Err(ref err) if err.is_file_nonexistant() => {
                let entry = SeriesEntry::new(info.id);
                EntryState::new(entry)
            }
            Err(err) => return Err(err),
        };

        state.sync_changes(remote, &name)?;

        Ok(SeriesTracker { info, state, name })
    }

    pub fn begin_watching<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;
        let last_status = state.status();

        match last_status {
            Status::Watching | Status::Rewatching => {
                // There is an edge case where all episodes have been watched, but the status
                // is still set to watching / rewatching. Here we just start a rewatch
                if state.watched_eps() >= self.info.episodes {
                    state.set_status(Status::Rewatching);
                    state.set_watched_eps(0);

                    if last_status == Status::Rewatching {
                        state.set_times_rewatched(state.times_rewatched() + 1);
                    }
                }
            }
            Status::Completed => {
                state.set_status(Status::Rewatching);
                state.set_times_rewatched(state.times_rewatched() + 1);
                state.set_watched_eps(0);
            }
            Status::PlanToWatch => state.set_status(Status::Watching),
            Status::OnHold | Status::Dropped => {
                state.set_status(Status::Watching);
                state.set_watched_eps(0);
            }
        }

        if state.start_date().is_none()
            || (last_status == Status::Completed
                && state.status() == Status::Rewatching
                && config.reset_dates_on_rewatch)
        {
            state.set_start_date(Some(Local::today().naive_local()));
        }

        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn episode_completed<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;
        state.set_watched_eps(state.watched_eps() + 1);

        if state.watched_eps() >= self.info.episodes {
            return self.series_complete(remote, config);
        }

        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn series_complete<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;

        // A rewatch is typically only counted once the series is completed again
        if state.status() == Status::Rewatching {
            state.set_times_rewatched(state.times_rewatched() + 1);
        }

        if state.end_date().is_none()
            || (state.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            state.set_end_date(Some(Local::today().naive_local()));
        }

        state.set_status(Status::Completed);
        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }
}
<commit_msg>[track] Don't increase rewatch count when series status is completed<commit_after>use crate::config::Config;
use crate::err::Result;
use crate::file::{FileType, SaveDir, SaveFile};
use crate::series::remote::{RemoteService, SeriesEntry, SeriesInfo, Status};
use chrono::{Local, NaiveDate};
use serde_derive::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
pub struct EntryState {
    entry: SeriesEntry,
    needs_sync: bool,
}

impl EntryState {
    pub fn new(entry: SeriesEntry) -> EntryState {
        EntryState {
            entry,
            needs_sync: false,
        }
    }

    pub fn sync_changes_to_remote<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        if !self.needs_sync {
            return Ok(());
        }

        let remote = remote.as_ref();

        if remote.is_offline() {
            self.save(name.as_ref())?;
            return Ok(());
        }

        remote.update_list_entry(&self.entry)?;

        self.needs_sync = false;
        self.save(name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes_from_remote<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        let remote = remote.as_ref();

        if self.needs_sync || remote.is_offline() {
            return Ok(());
        }

        let entry = match remote.get_list_entry(self.entry.id)? {
            Some(entry) => entry,
            None => return Ok(()),
        };

        self.entry = entry;
        self.save(name.as_ref())?;

        Ok(())
    }

    pub fn sync_changes<R, S>(&mut self, remote: R, name: S) -> Result<()>
    where
        R: AsRef<RemoteService>,
        S: AsRef<str>,
    {
        self.sync_changes_from_remote(&remote, &name)?;
        self.sync_changes_to_remote(&remote, &name)
    }

    #[inline(always)]
    pub fn watched_eps(&self) -> u32 {
        self.entry.watched_eps
    }

    #[inline(always)]
    pub fn set_watched_eps(&mut self, watched_eps: u32) {
        self.entry.watched_eps = watched_eps;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn score(&self) -> Option<f32> {
        self.entry.score
    }

    #[inline(always)]
    pub fn status(&self) -> Status {
        self.entry.status
    }

    #[inline(always)]
    pub fn set_status(&mut self, status: Status) {
        self.entry.status = status;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn times_rewatched(&self) -> u32 {
        self.entry.times_rewatched
    }

    #[inline(always)]
    pub fn set_times_rewatched(&mut self, times_rewatched: u32) {
        self.entry.times_rewatched = times_rewatched;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn start_date(&self) -> Option<NaiveDate> {
        self.entry.start_date
    }

    #[inline(always)]
    pub fn set_start_date(&mut self, start_date: Option<NaiveDate>) {
        self.entry.start_date = start_date;
        self.needs_sync = true;
    }

    #[inline(always)]
    pub fn end_date(&self) -> Option<NaiveDate> {
        self.entry.end_date
    }

    #[inline(always)]
    pub fn set_end_date(&mut self, end_date: Option<NaiveDate>) {
        self.entry.end_date = end_date;
        self.needs_sync = true;
    }
}

impl SaveFile for EntryState {
    fn filename() -> &'static str {
        "entry_state.mpack"
    }

    fn save_dir() -> SaveDir {
        SaveDir::LocalData
    }

    fn file_type() -> FileType {
        FileType::MessagePack
    }
}

#[derive(Debug)]
pub struct SeriesTracker<'a> {
    pub info: &'a SeriesInfo,
    pub state: EntryState,
    pub name: String,
}

impl<'a> SeriesTracker<'a> {
    pub fn init<R, S>(remote: R, info: &'a SeriesInfo, name: S) -> Result<SeriesTracker<'a>>
    where
        R: AsRef<RemoteService>,
        S: Into<String>,
    {
        let name = name.into();

        let mut state = match EntryState::load(name.as_ref()) {
            Ok(state) => state,
            Err(ref err) if err.is_file_nonexistant() => {
                let entry = SeriesEntry::new(info.id);
                EntryState::new(entry)
            }
            Err(err) => return Err(err),
        };

        state.sync_changes(remote, &name)?;

        Ok(SeriesTracker { info, state, name })
    }

    pub fn begin_watching<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;
        let last_status = state.status();

        match last_status {
            Status::Watching | Status::Rewatching => {
                // There is an edge case where all episodes have been watched, but the status
                // is still set to watching / rewatching. Here we just start a rewatch
                if state.watched_eps() >= self.info.episodes {
                    state.set_status(Status::Rewatching);
                    state.set_watched_eps(0);

                    if last_status == Status::Rewatching {
                        state.set_times_rewatched(state.times_rewatched() + 1);
                    }
                }
            }
            Status::Completed => {
                state.set_status(Status::Rewatching);
                state.set_watched_eps(0);
            }
            Status::PlanToWatch => state.set_status(Status::Watching),
            Status::OnHold | Status::Dropped => {
                state.set_status(Status::Watching);
                state.set_watched_eps(0);
            }
        }

        if state.start_date().is_none()
            || (last_status == Status::Completed
                && state.status() == Status::Rewatching
                && config.reset_dates_on_rewatch)
        {
            state.set_start_date(Some(Local::today().naive_local()));
        }

        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn episode_completed<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;
        state.set_watched_eps(state.watched_eps() + 1);

        if state.watched_eps() >= self.info.episodes {
            return self.series_complete(remote, config);
        }

        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }

    pub fn series_complete<R>(&mut self, remote: R, config: &Config) -> Result<()>
    where
        R: AsRef<RemoteService>,
    {
        let state = &mut self.state;

        // A rewatch is typically only counted once the series is completed again
        if state.status() == Status::Rewatching {
            state.set_times_rewatched(state.times_rewatched() + 1);
        }

        if state.end_date().is_none()
            || (state.status() == Status::Rewatching && config.reset_dates_on_rewatch)
        {
            state.set_end_date(Some(Local::today().naive_local()));
        }

        state.set_status(Status::Completed);
        state.sync_changes_to_remote(remote, &self.name)?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Useful stuff related to the spaceapi.

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

}
<commit_msg>spaceapi: Add `is_absent` to `Optional`<commit_after>//! Useful stuff related to the spaceapi.

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn is_absent(&self) -> bool {
        match *self {
            Optional::Absent => true,
            _ => false
        }
    }

}
<|endoftext|>


<commit_before>use iso8601;

pub fn escape_xml(s: &str) -> String {
    // FIXME: This shouldn't need to allocate in all cases
    s.replace("&", "&").replace("<", "<")
}

pub fn format_datetime(date_time: &iso8601::DateTime) -> String {
    let iso8601::Time { hour, minute, second, .. } = date_time.time;

    match date_time.date {
        iso8601::Date::YMD { year, month, day } => {
            format!("{:04}{:02}{:02}T{:02}:{:02}:{:02}",
                year, month, day,
                hour, minute, second
            )
        }
        _ => { unimplemented!() }
    }
}

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

    #[test]
    fn formats_datetimes() {
        let date_time = iso8601::datetime("2016-05-02T06:01:05-0800").unwrap();

        assert_eq!(format_datetime(&date_time), "20160502T06:01:05");
    }
}
<commit_msg>Use xml-rs's escaping function<commit_after>use iso8601;
use xml::escape::escape_str_pcdata;

use std::borrow::Cow;

/// Escape a string for use as XML characters.
///
/// The resulting string is *not* suitable for use in XML attributes, but XML-RPC doesn't use those.
pub fn escape_xml(s: &str) -> Cow<str> {
    escape_str_pcdata(s)
}

pub fn format_datetime(date_time: &iso8601::DateTime) -> String {
    let iso8601::Time { hour, minute, second, .. } = date_time.time;

    match date_time.date {
        iso8601::Date::YMD { year, month, day } => {
            format!("{:04}{:02}{:02}T{:02}:{:02}:{:02}",
                year, month, day,
                hour, minute, second
            )
        }
        _ => { unimplemented!() }
    }
}

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

    #[test]
    fn formats_datetimes() {
        let date_time = iso8601::datetime("2016-05-02T06:01:05-0800").unwrap();

        assert_eq!(format_datetime(&date_time), "20160502T06:01:05");
    }
}
<|endoftext|>


<commit_before>//! Tools for working with video files.

use num::rational::Ratio;
use regex::Regex;
use rustc_serialize::{Decodable, Decoder, json};
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::result;
use std::str::{FromStr, from_utf8};

use errors::*;
use lang::Lang;
use time::Period;

/// Information about an MP3 track (optional).
#[derive(Debug, Default)]
#[allow(missing_docs)]
pub struct Id3Metadata {
    pub genre: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
    pub track_number: Option<(usize, usize)>,
    pub track_name: Option<String>,
    pub lyrics: Option<String>,
}

impl Id3Metadata {
    fn add_args(&self, cmd: &mut Command) {
        if let Some(ref genre) = self.genre {
            cmd.arg("-metadata").arg(format!("genre={}", genre));
        }
        if let Some(ref artist) = self.artist {
            cmd.arg("-metadata").arg(format!("artist={}", artist));
        }
        if let Some(ref album) = self.album {
            cmd.arg("-metadata").arg(format!("album={}", album));
        }
        if let Some((track, total)) = self.track_number {
            cmd.arg("-metadata").arg(format!("track={}/{}", track, total));
        }
        if let Some(ref track_name) = self.track_name {
            cmd.arg("-metadata").arg(format!("title={}", track_name));
        }
        if let Some(ref lyrics) = self.lyrics {
            cmd.arg("-metadata").arg(format!("lyrics={}", lyrics));
        }
    }
}

/// Individual streams inside a video are labelled with a codec type.
#[derive(Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CodecType {
    Audio,
    Video,
    Subtitle,
    Other(String),
}

impl Decodable for CodecType {
    fn decode<D: Decoder>(d: &mut D) -> result::Result<Self, D::Error> {
        match &try!(d.read_str())[..] {
            "audio" => Ok(CodecType::Audio),
            "video" => Ok(CodecType::Video),
            "subtitle" => Ok(CodecType::Subtitle),
            s => Ok(CodecType::Other(s.to_owned())),
        }
    }
}

/// A wrapper around `Ratio` with custom serialization support.
#[derive(Debug)]
pub struct Fraction(Ratio<u32>);

impl Fraction {
    fn decode_parts<D>(d: &mut D) -> result::Result<(u32, u32), D::Error>
        where D: Decoder
    {
        let s = try!(d.read_str());
        let re = Regex::new(r"^(\d+)/(\d+)$").unwrap();
        let cap = try!(re.captures(&s).ok_or_else(|| {
            d.error(&format!("Expected fraction: {}", &s))
        }));
        Ok((FromStr::from_str(cap.at(1).unwrap()).unwrap(),
            FromStr::from_str(cap.at(2).unwrap()).unwrap()))
    }
}

impl Decodable for Fraction {
    fn decode<D: Decoder>(d: &mut D) -> result::Result<Self, D::Error> {
        let (num, denom) = try!(Fraction::decode_parts(d));
        if denom == 0 {
            Err(d.error("Found fraction with a denominator of 0"))
        } else {
            Ok(Fraction(Ratio::new(num, denom)))
        }
    }
}

/// An individual content stream within a video.
#[derive(Debug, RustcDecodable)]
#[allow(missing_docs, dead_code)]
pub struct Stream {
    pub index: usize,
    pub codec_name: String,
    pub codec_long_name: Option<String>,
    pub codec_type: CodecType,
    pub codec_time_base: Fraction,
    pub codec_tag_string: String,
    pub codec_tag: String,
    pub profile: Option<String>,
    pub width: Option<usize>,
    pub height: Option<usize>,
    //has_b_frames
    //sample_aspect_ratio
    //display_aspect_ratio
    pub pix_fmt: Option<String>,
    pub level: Option<u32>,
    pub sample_rate: Option<f64>,
    pub channels: Option<usize>,
    pub bits_per_sample: Option<u32>,
    //avg_frame_rate
    pub time_base: Fraction,
    pub start_time: f64,
    //duration
    pub tags: Option<BTreeMap<String, String>>,
}

impl Stream {
    /// Return the language associated with this stream, if we can figure
    /// it out.
    pub fn language(&self) -> Option<Lang> {
        self.tags.as_ref()
            .and_then(|tags| tags.get("language"))
            .and_then(|lang| Lang::iso639(lang).ok())
    }
}

#[test]
fn test_stream_decode() {
    let json = "
{
  \"index\" : 2,
  \"codec_name\" : \"aac\",
  \"codec_long_name\" : \"AAC (Advanced Audio Coding)\",
  \"codec_type\" : \"audio\",
  \"codec_time_base\" : \"1/48000\",
  \"codec_tag_string\" : \"[0][0][0][0]\",
  \"codec_tag\" : \"0x0000\",
  \"sample_rate\" : \"48000.000000\",
  \"channels\" : 2,
  \"bits_per_sample\" : 0,
  \"avg_frame_rate\" : \"0/0\",
  \"time_base\" : \"1/1000\",
  \"start_time\" : \"0.000000\",
  \"duration\" : \"N/A\",
  \"tags\" : {
    \"language\" : \"eng\"
  }
}
";
    let stream: Stream = json::decode(json).unwrap();
    assert_eq!(CodecType::Audio, stream.codec_type);
    assert_eq!(Some(Lang::iso639("en").unwrap()),
               stream.language())
}

/// What kind of data do we want to extract, and from what position in the
/// video clip?
pub enum ExtractionSpec {
    /// Extract an image at the specified time.
    Image(f32),
    /// Extract an audio clip covering the specified stream and period.
    Audio(Option<usize>, Period, Id3Metadata),
}

impl ExtractionSpec {
    /// The earliest time at which we might need to extract data.
    fn earliest_time(&self) -> f32 {
        match self {
            &ExtractionSpec::Image(time) => time,
            &ExtractionSpec::Audio(_, period, _) => period.begin(),
        }
    }

    /// Can we combine this extraction with others in a giant batch
    /// request?
    fn can_be_batched(&self) -> bool {
        match self {
            // Batch processing of images requires decoding the whole
            // video, but we can do a "fast seek" and extract one image
            // extremely quickly.
            &ExtractionSpec::Image(_) => false,
            _ => true,
        }
    }

    /// Figure out what ffmpeg args we would need to extract the requested
    /// data.  Assume that the "fast seek" feature has been used to start
    /// decoding at `time_base`.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        match self {
            &ExtractionSpec::Image(time) => {
                let scale_filter =
                    format!("scale=iw*min(1\\,min({}/iw\\,{}/ih)):-1",
                            240, 160);
                cmd.arg("-ss").arg(format!("{}", time - time_base))
                    .arg("-vframes").arg("1")
                    .arg("-filter_complex").arg(&scale_filter)
                    .arg("-f").arg("image2");
            }
            &ExtractionSpec::Audio(stream, period, ref metadata) => {
                if let Some(sid) = stream {
                    cmd.arg("-map").arg(format!("0:{}", sid));
                }
                metadata.add_args(cmd);
                cmd.arg("-ss").arg(format!("{}", period.begin() - time_base))
                    .arg("-t").arg(format!("{}", period.duration()));
            }
        }
    }
}

/// Information about what kind of data we want to extract.
pub struct Extraction {
    /// The path to extract to.
    pub path: PathBuf,
    /// What kind of data to extract.
    pub spec: ExtractionSpec,
}

impl Extraction {
    /// Add the necessary args to `cmd` to perform this extraction.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        self.spec.add_args(cmd, time_base);
        cmd.arg(self.path.clone());
    }
}

/// Metadata associated with a video.
#[derive(Debug, RustcDecodable)]
struct Metadata {
    streams: Vec<Stream>,
}

/// Represents a video file on disk.
#[derive(Debug)]
pub struct Video {
    path: PathBuf,
    metadata: Metadata,
}

impl Video {
    /// Create a new video file, given a path.
    pub fn new(path: &Path) -> Result<Video> {
        // Ensure we have an actual file name before doing anything else.
        try!(path.file_name().ok_or_else(|| {
            err_str(format!("Video path does not have a filename: {}",
                            path.to_string_lossy()))
        }));

        // Run our probe command.
        let cmd = Command::new("ffprobe")
            .arg("-v").arg("quiet")
            .arg("-show_streams")
            .arg("-of").arg("json")
            .arg(path)
            .output();
        let output = try!(cmd);
        let stdout = try!(from_utf8(&output.stdout));
        let metadata = try!(json::decode(stdout));

        Ok(Video { path: path.to_owned(), metadata: metadata })
    }

    /// Get just the file name of this video file.
    pub fn file_name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }

    /// Get just the file stem of this video file, stripped of any
    /// extensions.
    pub fn file_stem(&self) -> &OsStr {
        self.path.file_stem().unwrap()
    }

    /// List all the tracks in a video file.
    pub fn streams(&self) -> &[Stream] {
        &self.metadata.streams
    }

    /// Choose the best audio for the specified language.
    pub fn audio_for(&self, lang: Lang) -> Option<usize> {
        self.streams().iter().position(|s| {
            s.codec_type == CodecType::Audio &&
                s.language() == Some(lang)
        })
    }

    /// Create an extraction command using the specified `time_base`.  This
    /// allows us to start extractions at any arbitrary point in the video
    /// rapidly.
    fn extract_command(&self, time_base: f32) -> Command {
        let mut cmd = Command::new("ffmpeg");
        cmd.arg("-ss").arg(format!("{}", time_base));
        cmd.arg("-i").arg(&self.path);
        cmd
    }

    /// Perform a single extraction.
    fn extract_one(&self, extraction: &Extraction) -> Result<()> {
        let time_base = extraction.spec.earliest_time();
        let mut cmd = self.extract_command(time_base);
        extraction.add_args(&mut cmd, time_base);
        try!(cmd.output());
        Ok(())
    }

    /// Perform a batch extraction.  We assume that the extractions are
    /// sorted in temporal order.
    fn extract_batch(&self, extractions: &[&Extraction]) ->  Result<()> {
        // Bail early if we have nothing to extract
        if extractions.is_empty() { return Ok(()); }
        let time_base = extractions[0].spec.earliest_time();

        // Build and run our batch extraction command.
        let mut cmd = self.extract_command(time_base);
        for e in extractions {
            assert!(e.spec.can_be_batched());
            e.add_args(&mut cmd, time_base);
        }
        try!(cmd.output());
        Ok(())
    }

    /// Perform a list of extractions as efficiently as possible.  We use a
    /// batch interface to avoid making too many passes through the file.
    /// We assume that the extractions are sorted in temporal order.
    pub fn extract(&self, extractions: &[Extraction]) -> Result<()> {
        let mut batch: Vec<&Extraction> = vec!();
        for e in extractions {
            if e.spec.can_be_batched() {
                batch.push(e);
            } else {
                try!(self.extract_one(e));
            }
        }

        for chunk in batch.chunks(20) {
            try!(self.extract_batch(chunk));
        }
        Ok(())
    }
}
<commit_msg>fix optional json fields on mp4 files with extra streams<commit_after>//! Tools for working with video files.

use num::rational::Ratio;
use regex::Regex;
use rustc_serialize::{Decodable, Decoder, json};
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::result;
use std::str::{FromStr, from_utf8};

use errors::*;
use lang::Lang;
use time::Period;

/// Information about an MP3 track (optional).
#[derive(Debug, Default)]
#[allow(missing_docs)]
pub struct Id3Metadata {
    pub genre: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
    pub track_number: Option<(usize, usize)>,
    pub track_name: Option<String>,
    pub lyrics: Option<String>,
}

impl Id3Metadata {
    fn add_args(&self, cmd: &mut Command) {
        if let Some(ref genre) = self.genre {
            cmd.arg("-metadata").arg(format!("genre={}", genre));
        }
        if let Some(ref artist) = self.artist {
            cmd.arg("-metadata").arg(format!("artist={}", artist));
        }
        if let Some(ref album) = self.album {
            cmd.arg("-metadata").arg(format!("album={}", album));
        }
        if let Some((track, total)) = self.track_number {
            cmd.arg("-metadata").arg(format!("track={}/{}", track, total));
        }
        if let Some(ref track_name) = self.track_name {
            cmd.arg("-metadata").arg(format!("title={}", track_name));
        }
        if let Some(ref lyrics) = self.lyrics {
            cmd.arg("-metadata").arg(format!("lyrics={}", lyrics));
        }
    }
}

/// Individual streams inside a video are labelled with a codec type.
#[derive(Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CodecType {
    Audio,
    Video,
    Subtitle,
    Other(String),
}

impl Decodable for CodecType {
    fn decode<D: Decoder>(d: &mut D) -> result::Result<Self, D::Error> {
        match &try!(d.read_str())[..] {
            "audio" => Ok(CodecType::Audio),
            "video" => Ok(CodecType::Video),
            "subtitle" => Ok(CodecType::Subtitle),
            s => Ok(CodecType::Other(s.to_owned())),
        }
    }
}

/// A wrapper around `Ratio` with custom serialization support.
#[derive(Debug)]
pub struct Fraction(Ratio<u32>);

impl Fraction {
    fn decode_parts<D>(d: &mut D) -> result::Result<(u32, u32), D::Error>
        where D: Decoder
    {
        let s = try!(d.read_str());
        let re = Regex::new(r"^(\d+)/(\d+)$").unwrap();
        let cap = try!(re.captures(&s).ok_or_else(|| {
            d.error(&format!("Expected fraction: {}", &s))
        }));
        Ok((FromStr::from_str(cap.at(1).unwrap()).unwrap(),
            FromStr::from_str(cap.at(2).unwrap()).unwrap()))
    }
}

impl Decodable for Fraction {
    fn decode<D: Decoder>(d: &mut D) -> result::Result<Self, D::Error> {
        let (num, denom) = try!(Fraction::decode_parts(d));
        if denom == 0 {
            Err(d.error("Found fraction with a denominator of 0"))
        } else {
            Ok(Fraction(Ratio::new(num, denom)))
        }
    }
}

/// An individual content stream within a video.
#[derive(Debug, RustcDecodable)]
#[allow(missing_docs, dead_code)]
pub struct Stream {
    pub index: usize,
    pub codec_name: Option<String>,
    pub codec_long_name: Option<String>,
    pub codec_type: CodecType,
    pub codec_time_base: Option<Fraction>,
    pub codec_tag_string: String,
    pub codec_tag: String,
    pub profile: Option<String>,
    pub width: Option<usize>,
    pub height: Option<usize>,
    //has_b_frames
    //sample_aspect_ratio
    //display_aspect_ratio
    pub pix_fmt: Option<String>,
    pub level: Option<u32>,
    pub sample_rate: Option<f64>,
    pub channels: Option<usize>,
    pub bits_per_sample: Option<u32>,
    //avg_frame_rate
    pub time_base: Fraction,
    pub start_time: f64,
    //duration
    pub tags: Option<BTreeMap<String, String>>,
}

impl Stream {
    /// Return the language associated with this stream, if we can figure
    /// it out.
    pub fn language(&self) -> Option<Lang> {
        self.tags.as_ref()
            .and_then(|tags| tags.get("language"))
            .and_then(|lang| Lang::iso639(lang).ok())
    }
}

#[test]
fn test_stream_decode() {
    let json = "
{
  \"index\" : 2,
  \"codec_name\" : \"aac\",
  \"codec_long_name\" : \"AAC (Advanced Audio Coding)\",
  \"codec_type\" : \"audio\",
  \"codec_time_base\" : \"1/48000\",
  \"codec_tag_string\" : \"[0][0][0][0]\",
  \"codec_tag\" : \"0x0000\",
  \"sample_rate\" : \"48000.000000\",
  \"channels\" : 2,
  \"bits_per_sample\" : 0,
  \"avg_frame_rate\" : \"0/0\",
  \"time_base\" : \"1/1000\",
  \"start_time\" : \"0.000000\",
  \"duration\" : \"N/A\",
  \"tags\" : {
    \"language\" : \"eng\"
  }
}
";
    let stream: Stream = json::decode(json).unwrap();
    assert_eq!(CodecType::Audio, stream.codec_type);
    assert_eq!(Some(Lang::iso639("en").unwrap()),
               stream.language())
}

/// What kind of data do we want to extract, and from what position in the
/// video clip?
pub enum ExtractionSpec {
    /// Extract an image at the specified time.
    Image(f32),
    /// Extract an audio clip covering the specified stream and period.
    Audio(Option<usize>, Period, Id3Metadata),
}

impl ExtractionSpec {
    /// The earliest time at which we might need to extract data.
    fn earliest_time(&self) -> f32 {
        match self {
            &ExtractionSpec::Image(time) => time,
            &ExtractionSpec::Audio(_, period, _) => period.begin(),
        }
    }

    /// Can we combine this extraction with others in a giant batch
    /// request?
    fn can_be_batched(&self) -> bool {
        match self {
            // Batch processing of images requires decoding the whole
            // video, but we can do a "fast seek" and extract one image
            // extremely quickly.
            &ExtractionSpec::Image(_) => false,
            _ => true,
        }
    }

    /// Figure out what ffmpeg args we would need to extract the requested
    /// data.  Assume that the "fast seek" feature has been used to start
    /// decoding at `time_base`.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        match self {
            &ExtractionSpec::Image(time) => {
                let scale_filter =
                    format!("scale=iw*min(1\\,min({}/iw\\,{}/ih)):-1",
                            240, 160);
                cmd.arg("-ss").arg(format!("{}", time - time_base))
                    .arg("-vframes").arg("1")
                    .arg("-filter_complex").arg(&scale_filter)
                    .arg("-f").arg("image2");
            }
            &ExtractionSpec::Audio(stream, period, ref metadata) => {
                if let Some(sid) = stream {
                    cmd.arg("-map").arg(format!("0:{}", sid));
                }
                metadata.add_args(cmd);
                cmd.arg("-ss").arg(format!("{}", period.begin() - time_base))
                    .arg("-t").arg(format!("{}", period.duration()));
            }
        }
    }
}

/// Information about what kind of data we want to extract.
pub struct Extraction {
    /// The path to extract to.
    pub path: PathBuf,
    /// What kind of data to extract.
    pub spec: ExtractionSpec,
}

impl Extraction {
    /// Add the necessary args to `cmd` to perform this extraction.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        self.spec.add_args(cmd, time_base);
        cmd.arg(self.path.clone());
    }
}

/// Metadata associated with a video.
#[derive(Debug, RustcDecodable)]
struct Metadata {
    streams: Vec<Stream>,
}

/// Represents a video file on disk.
#[derive(Debug)]
pub struct Video {
    path: PathBuf,
    metadata: Metadata,
}

impl Video {
    /// Create a new video file, given a path.
    pub fn new(path: &Path) -> Result<Video> {
        // Ensure we have an actual file name before doing anything else.
        try!(path.file_name().ok_or_else(|| {
            err_str(format!("Video path does not have a filename: {}",
                            path.to_string_lossy()))
        }));

        // Run our probe command.
        let cmd = Command::new("ffprobe")
            .arg("-v").arg("quiet")
            .arg("-show_streams")
            .arg("-of").arg("json")
            .arg(path)
            .output();
        let output = try!(cmd);
        let stdout = try!(from_utf8(&output.stdout));
        let metadata = try!(json::decode(stdout));

        Ok(Video { path: path.to_owned(), metadata: metadata })
    }

    /// Get just the file name of this video file.
    pub fn file_name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }

    /// Get just the file stem of this video file, stripped of any
    /// extensions.
    pub fn file_stem(&self) -> &OsStr {
        self.path.file_stem().unwrap()
    }

    /// List all the tracks in a video file.
    pub fn streams(&self) -> &[Stream] {
        &self.metadata.streams
    }

    /// Choose the best audio for the specified language.
    pub fn audio_for(&self, lang: Lang) -> Option<usize> {
        self.streams().iter().position(|s| {
            s.codec_type == CodecType::Audio &&
                s.language() == Some(lang)
        })
    }

    /// Create an extraction command using the specified `time_base`.  This
    /// allows us to start extractions at any arbitrary point in the video
    /// rapidly.
    fn extract_command(&self, time_base: f32) -> Command {
        let mut cmd = Command::new("ffmpeg");
        cmd.arg("-ss").arg(format!("{}", time_base));
        cmd.arg("-i").arg(&self.path);
        cmd
    }

    /// Perform a single extraction.
    fn extract_one(&self, extraction: &Extraction) -> Result<()> {
        let time_base = extraction.spec.earliest_time();
        let mut cmd = self.extract_command(time_base);
        extraction.add_args(&mut cmd, time_base);
        try!(cmd.output());
        Ok(())
    }

    /// Perform a batch extraction.  We assume that the extractions are
    /// sorted in temporal order.
    fn extract_batch(&self, extractions: &[&Extraction]) ->  Result<()> {
        // Bail early if we have nothing to extract
        if extractions.is_empty() { return Ok(()); }
        let time_base = extractions[0].spec.earliest_time();

        // Build and run our batch extraction command.
        let mut cmd = self.extract_command(time_base);
        for e in extractions {
            assert!(e.spec.can_be_batched());
            e.add_args(&mut cmd, time_base);
        }
        try!(cmd.output());
        Ok(())
    }

    /// Perform a list of extractions as efficiently as possible.  We use a
    /// batch interface to avoid making too many passes through the file.
    /// We assume that the extractions are sorted in temporal order.
    pub fn extract(&self, extractions: &[Extraction]) -> Result<()> {
        let mut batch: Vec<&Extraction> = vec!();
        for e in extractions {
            if e.spec.can_be_batched() {
                batch.push(e);
            } else {
                try!(self.extract_one(e));
            }
        }

        for chunk in batch.chunks(20) {
            try!(self.extract_batch(chunk));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::{
    render_object::*,
    utils::{Point, Rectangle},
};

/// The target of the popup, given as an entity or as fixed point coordinate.
#[derive(Clone, Debug, PartialEq)]
pub enum PopupTarget {
    /// the entity of the target (Entity).
    Entity(Entity),

    /// the point coordinate (f64, f64).
    Point(Point),
}

impl Default for PopupTarget {
    fn default() -> Self {
	Self::Point(Point::new(0.0, 30.0))
    }
}

impl From<Entity> for PopupTarget {
    fn from(entity: Entity) -> Self {
	Self::Entity(entity)
    }
}

impl From<Point> for PopupTarget {
    fn from(point: Point) -> Self {
	Self::Point(point)
    }
}

impl IntoPropertySource<PopupTarget> for Entity {
    fn into_source(self) -> PropertySource<PopupTarget> {
	PropertySource::Value(self.into())
    }
}

impl IntoPropertySource<PopupTarget> for Point {
    fn into_source(self) -> PropertySource<PopupTarget> {
	PropertySource::Value(self.into())
    }
}

/// Popups placement relative to the target.
///
/// An otional distance attribute (float), defines the margin
/// between the selection box and the drop down popup.
#[derive(Clone, Debug, PartialEq)]
pub enum Placement {
    Top(f64),
    Bottom(f64),
    Left(f64),
    Right(f64),
}

impl Default for Placement {
    fn default() -> Self {
	Self::Bottom(5.0)
    }
}
impl Placement {
    /// Calculate the `margin` between popup and parent target (64bit float).
    pub fn get_margin(&self) -> f64 {
	match self {
	    Self::Top(margin) => *margin,
	    Self::Bottom(margin) => *margin,
	    Self::Left(margin) => *margin,
	    Self::Right(margin) => *margin,
	}
    }

    /// Place the popup widget relative to the `top` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    // before the function was called into_top()
    pub fn to_top(self) -> Self {
	Self::Top(self.get_margin())
    }

    /// Place the popup widget relative to the `bottom` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn to_bottom(self) -> Self {
	Self::Bottom(self.get_margin())
    }
    /// Place the popup widget relative to the left of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn to_left(self) -> Self {
	Self::Left(self.get_margin())
    }
    /// Place the popup widget relative to the `right` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn to_right(self) -> Self {
	Self::Right(self.get_margin())
    }
}

/*
impl From<usize> for Placement {
    fn from(index: usize) -> Self {
	match entity
	{
	    0 => Self::Top,
	    1 => Self::Botton,
	    2 => Self::Left,
	    3 => Self::Right,
	    _ => panic!()
	}
    }
}
*/
into_property_source!(Placement);

/// The `PopupRenderObject` is used to render the contents of a `Popup` widget inside a recangle.
pub struct PopupRenderObject(RectangleRenderObject);

impl PopupRenderObject {
    pub fn new() -> Self {
	Self::default()
    }
}
impl Default for PopupRenderObject {
    fn default() -> Self {
	Self(RectangleRenderObject)
    }
}

impl Into<Box<dyn RenderObject>> for PopupRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
	Box::new(self)
    }
}

impl RenderObject for PopupRenderObject {
    fn render_self(&self, ctx: &mut Context, global_position: &Point) {
	if let Some(target) = ctx.widget().try_clone::<PopupTarget>("target") {
	    let current_bounds: Rectangle = ctx.widget().clone("bounds");
	    let current_constraint: Constraint = ctx.widget().clone("constraint");

	    // println!("render popup: bounds.width={:?} bounds.height={:?}",
	    //          current_bounds.width(),
	    //          current_bounds.height());

	    let real_target_bounds = match target {
		PopupTarget::Entity(entity) => {
		    let target_position: Point = ctx.get_widget(entity.into()).clone("position");

		    //WARNING: this is true only if called during post_layout_update, otherwise the bounds will refere to space available to the widget, not the effective size
		    let mut target_bounds: Rectangle =
			ctx.get_widget(entity.into()).clone("bounds");
		    target_bounds.set_position(target_position);
		    target_bounds
		}
		PopupTarget::Point(mut point) => {
		    point.set_x(point.x() + current_bounds.width() / 2.0);
		    point.set_y(point.y() + current_bounds.height() / 2.0);
		    Rectangle::new(point, (0.0, 0.0))
		}
	    };

	    let relative_position: Placement =
		ctx.widget().clone_or_default("relative_position");

	    let new_popup_bounds = match relative_position {
		Placement::Left(margin) => {
		    let current_v_align: Alignment = ctx.widget().clone("v_align");

		    let x = real_target_bounds.x() - current_bounds.width() - margin;
		    let y = current_v_align.align_position(
			real_target_bounds.height(),
			current_bounds.height(),
			real_target_bounds.y(),
			real_target_bounds.y() + real_target_bounds.height(),
		    );

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Right(margin) => {
		    let current_v_align: Alignment = ctx.widget().clone("v_align");

		    let x = real_target_bounds.x() + real_target_bounds.width() + margin;
		    let y = current_v_align.align_position(
			real_target_bounds.height(),
			current_bounds.height(),
			real_target_bounds.y(),
			real_target_bounds.y() + real_target_bounds.height(),
		    );

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Top(margin) => {
		    let current_h_align: Alignment = ctx.widget().clone("h_align");

		    let x = current_h_align.align_position(
			real_target_bounds.width(),
			current_bounds.width(),
			real_target_bounds.x(),
			real_target_bounds.x() + real_target_bounds.width(),
		    );
		    let y = real_target_bounds.y() - current_bounds.height() - margin;
		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Bottom(margin) => {
		    let current_h_align: Alignment = ctx.widget().clone("h_align");

		    let x = current_h_align.align_position(
			real_target_bounds.width(),
			current_bounds.width(),
			real_target_bounds.x(),
			real_target_bounds.x() + real_target_bounds.width(),
		    );
		    let y = real_target_bounds.y() + real_target_bounds.height() + margin;
		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
	    };

	    ctx.widget().set::<Rectangle>("bounds", new_popup_bounds);
	} else {
	    println!("Target not found");
	}

	self.0.render_self(ctx, global_position);
    }
}
<commit_msg>orbtk_core: new associated popup functions to render_object<commit_after>use crate::{
    render_object::*,
    utils::Point,
};

/// The target of the popup, given as an entity or as fixed point coordinate.
#[derive(Clone, Debug, PartialEq)]
pub enum PopupTarget {
    /// the entity of the target (Entity).
    Entity(Entity),

    /// the point coordinate (f64, f64).
    Point(Point),
}

impl Default for PopupTarget {
    fn default() -> Self {
	Self::Point(Point::new(0.0, 0.0))
    }
}

impl From<Entity> for PopupTarget {
    fn from(entity: Entity) -> Self {
	Self::Entity(entity)
    }
}

impl From<Point> for PopupTarget {
    fn from(point: Point) -> Self {
	Self::Point(point)
    }
}

impl IntoPropertySource<PopupTarget> for Entity {
    fn into_source(self) -> PropertySource<PopupTarget> {
	PropertySource::Value(self.into())
    }
}

impl IntoPropertySource<PopupTarget> for Point {
    fn into_source(self) -> PropertySource<PopupTarget> {
	PropertySource::Value(self.into())
    }
}

/// Popups placement relative to the target.
///
/// An otional distance attribute (float), defines the margin
/// between the selection box and the drop down popup.
#[derive(Clone, Debug, PartialEq)]
pub enum Placement {
    Bottom(f64),
    Left(f64),
    Right(f64),
    Top(f64),
}

impl Default for Placement {
    fn default() -> Self {
	Self::Bottom(5.0)
    }
}
impl Placement {
    /// Calculate the `margin` between popup and parent target (64bit float).
    pub fn get_margin(&self) -> f64 {
	match self {
	    Self::Top(margin) => *margin,
	    Self::Bottom(margin) => *margin,
	    Self::Left(margin) => *margin,
	    Self::Right(margin) => *margin,
	}
    }

    /// Returns the placement name.
    pub fn get_name(&self, index: i32) -> String {
	match index {
	    0 => "Bottom".to_string(),
	    1 => "Left".to_string(),
	    2 => "Right".to_string(),
	    3 => "Top".to_string(),
	    _ => {
		eprintln!("popup: placement variant with index {} not coverd!",
			  index);
		std::process::exit(1);
	    }
	}
    }

    /// Returns the placement index.
    pub fn get_index(&self, string: &str) -> u32  {
	match string {
	    "Bottom" => 0,
	    "Left" => 1,
	    "Right" => 2,
	    "Top" => 3,
	    _ => {
		eprintln!("popup: placement variant {} not coverd!",
			  string);
		std::process::exit(1);
	    }
	}
    }

    /// Place the popup widget relative to the `top` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    // before the function was called into_top()
    pub fn top(self) -> Self {
	Self::Top(self.get_margin())
    }

    /// Place the popup widget relative to the `bottom` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn bottom(self) -> Self {
	Self::Bottom(self.get_margin())
    }
    /// Place the popup widget relative to the left of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn left(self) -> Self {
	Self::Left(self.get_margin())
    }
    /// Place the popup widget relative to the `right` of its parent target.
    /// An optional `margin` adds a distance between popup and parent target.
    pub fn right(self) -> Self {
	Self::Right(self.get_margin())
    }
}

into_property_source!(Placement);

/// The `PopupRenderObject` is used to render the contents of a `Popup` widget inside a recangle.
pub struct PopupRenderObject(RectangleRenderObject);

impl PopupRenderObject {
    pub fn new() -> Self {
	Self::default()
    }
}
impl Default for PopupRenderObject {
    fn default() -> Self {
	Self(RectangleRenderObject)
    }
}

impl Into<Box<dyn RenderObject>> for PopupRenderObject {
    fn into(self) -> Box<dyn RenderObject> {
	Box::new(self)
    }
}

impl RenderObject for PopupRenderObject {
    fn render_self(&self, ctx: &mut Context, global_position: &Point) {
	if let Some(target) = ctx.widget().try_clone::<PopupTarget>("target") {
	    let current_bounds: Rectangle = ctx.widget().clone("bounds");
	    let current_constraint: Constraint = ctx.widget().clone("constraint");

	    // println!("render popup: bounds.width={:?} bounds.height={:?}",
	    //          current_bounds.width(),
	    //          current_bounds.height());

	    let real_target_bounds = match target {
		PopupTarget::Entity(entity) => {
		    let target_position: Point = ctx.get_widget(entity.into()).clone("position");

		    //WARNING: this is true only if called during post_layout_update, otherwise the bounds will refere to space available to the widget, not the effective size
		    let mut target_bounds: Rectangle =
			ctx.get_widget(entity.into()).clone("bounds");
		    target_bounds.set_position(target_position);
		    target_bounds
		}
		PopupTarget::Point(mut point) => {
		    point.set_x(point.x() + current_bounds.width() / 2.0);
		    point.set_y(point.y() + current_bounds.height() / 2.0);
		    Rectangle::new(point, (0.0, 0.0))
		}
	    };

	    let relative_position: Placement =
		ctx.widget().clone_or_default("relative_position");

	    let new_popup_bounds = match relative_position {
		Placement::Left(margin) => {
		    let current_v_align: Alignment = ctx.widget().clone("v_align");

		    let x = real_target_bounds.x() - current_bounds.width() - margin;
		    let y = current_v_align.align_position(
			real_target_bounds.height(),
			current_bounds.height(),
			real_target_bounds.y(),
			real_target_bounds.y() + real_target_bounds.height(),
		    );

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Right(margin) => {
		    let current_v_align: Alignment = ctx.widget().clone("v_align");

		    let x = real_target_bounds.x() + real_target_bounds.width() + margin;
		    let y = current_v_align.align_position(
			real_target_bounds.height(),
			current_bounds.height(),
			real_target_bounds.y(),
			real_target_bounds.y() + real_target_bounds.height(),
		    );

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Top(margin) => {
		    let current_h_align: Alignment = ctx.widget().clone("h_align");

		    let x = current_h_align.align_position(
			real_target_bounds.width(),
			current_bounds.width(),
			real_target_bounds.x(),
			real_target_bounds.x() + real_target_bounds.width(),
		    );
		    let y = real_target_bounds.y() - current_bounds.height() - margin;
		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
		Placement::Bottom(margin) => {
		    let current_h_align: Alignment = ctx.widget().clone("h_align");

		    let x = current_h_align.align_position(
			real_target_bounds.width(),
			current_bounds.width(),
			real_target_bounds.x(),
			real_target_bounds.x() + real_target_bounds.width(),
		    );
		    let y = real_target_bounds.y() + real_target_bounds.height() + margin;
		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    Rectangle::new((x, y), current_constraint.perform((width, height)))
		}
	    };

	    ctx.widget().set::<Rectangle>("bounds", new_popup_bounds);
	} else {
	    println!("Target not found");
	}

	self.0.render_self(ctx, global_position);
    }
}
<|endoftext|>


<commit_before>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, sanitize_sh};
use crate::tarball::GeneratedTarball;
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::{Config, TargetSelection};

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    stage: u32,
    host: Option<TargetSelection>,
    tarball: &GeneratedTarball,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let datadir_default = PathBuf::from("share");
    let docdir_default = datadir_default.join("doc/rust");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = datadir_default.join("man");
    let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default);
    let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
    let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = &builder.config.bindir;
    let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let datadir = prefix.join(datadir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let datadir = add_destdir(&datadir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let prefix = {
        fs::create_dir_all(&prefix)
            .unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err));
        fs::canonicalize(&prefix)
            .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err))
    };

    let empty_dir = builder.out.join("tmp/empty_dir");

    t!(fs::create_dir_all(&empty_dir));

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
        .arg(format!("--prefix={}", sanitize_sh(&prefix)))
        .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
        .arg(format!("--datadir={}", sanitize_sh(&datadir)))
        .arg(format!("--docdir={}", sanitize_sh(&docdir)))
        .arg(format!("--bindir={}", sanitize_sh(&bindir)))
        .arg(format!("--libdir={}", sanitize_sh(&libdir)))
        .arg(format!("--mandir={}", sanitize_sh(&mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        if let Component::Normal(s) = part {
            ret.push(s)
        }
    }
    ret
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: TargetSelection,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
        install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
    };
    Std, "library/std", true, only_hosts: false, {
        for target in &builder.targets {
            let tarball = builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            }).expect("missing std");
            install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
        let tarball = builder
            .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
            .expect("missing rust-analyzer");
        install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        let tarball = builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        }).expect("missing analysis");
        install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        let tarball = builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        let tarball = builder.ensure(dist::Src);
        install_sh(builder, "src", self.stage, None, &tarball);
    }
}
<commit_msg>bootstrap: fix x.py install not working with relative prefix<commit_after>//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Component, PathBuf};
use std::process::Command;

use build_helper::t;

use crate::dist::{self, sanitize_sh};
use crate::tarball::GeneratedTarball;
use crate::Compiler;

use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::{Config, TargetSelection};

fn install_sh(
    builder: &Builder<'_>,
    package: &str,
    stage: u32,
    host: Option<TargetSelection>,
    tarball: &GeneratedTarball,
) {
    builder.info(&format!("Install {} stage{} ({:?})", package, stage, host));

    let prefix = default_path(&builder.config.prefix, "/usr/local");
    let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
    let datadir = prefix.join(default_path(&builder.config.datadir, "share"));
    let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc"));
    let mandir = prefix.join(default_path(&builder.config.mandir, "share/man"));
    let libdir = prefix.join(default_path(&builder.config.libdir, "lib"));
    let bindir = prefix.join(&builder.config.bindir); // Default in config.rs

    let empty_dir = builder.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
        .arg(format!("--prefix={}", prepare_dir(prefix)))
        .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir)))
        .arg(format!("--datadir={}", prepare_dir(datadir)))
        .arg(format!("--docdir={}", prepare_dir(docdir)))
        .arg(format!("--bindir={}", prepare_dir(bindir)))
        .arg(format!("--libdir={}", prepare_dir(libdir)))
        .arg(format!("--mandir={}", prepare_dir(mandir)))
        .arg("--disable-ldconfig");
    builder.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
    PathBuf::from(config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)))
}

fn prepare_dir(mut path: PathBuf) -> String {
    // The DESTDIR environment variable is a standard way to install software in a subdirectory
    // while keeping the original directory structure, even if the prefix or other directories
    // contain absolute paths.
    //
    // More information on the environment variable is available here:
    // https://www.gnu.org/prep/standards/html_node/DESTDIR.html
    if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) {
        let without_destdir = path.clone();
        path = destdir;
        // Custom .join() which ignores disk roots.
        for part in without_destdir.components() {
            if let Component::Normal(s) = part {
                path.push(s)
            }
        }
    }

    // The installation command is not executed from the current directory, but from a temporary
    // directory. To prevent relative paths from breaking this converts relative paths to absolute
    // paths. std::fs::canonicalize is not used as that requires the path to actually be present.
    if path.is_relative() {
        path = std::env::current_dir().expect("failed to get the current directory").join(path);
        assert!(path.is_absolute(), "could not make the path relative");
    }

    sanitize_sh(&path)
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub compiler: Compiler,
            pub target: TargetSelection,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name {
                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
                    target: run.target,
                });
            }

            fn run($sel, $builder: &Builder<'_>) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
        install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
    };
    Std, "library/std", true, only_hosts: false, {
        for target in &builder.targets {
            let tarball = builder.ensure(dist::Std {
                compiler: self.compiler,
                target: *target
            }).expect("missing std");
            install_sh(builder, "std", self.compiler.stage, Some(*target), &tarball);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
        install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rls { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "rls", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install RLS stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    RustAnalyzer, "rust-analyzer", Self::should_build(_config), only_hosts: true, {
        let tarball = builder
            .ensure(dist::RustAnalyzer { compiler: self.compiler, target: self.target })
            .expect("missing rust-analyzer");
        install_sh(builder, "rust-analyzer", self.compiler.stage, Some(self.target), &tarball);
    };
    Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
        let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
        install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
    };
    Miri, "miri", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
            install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if let Some(tarball) = builder.ensure(dist::Rustfmt {
            compiler: self.compiler,
            target: self.target
        }) {
            install_sh(builder, "rustfmt", self.compiler.stage, Some(self.target), &tarball);
        } else {
            builder.info(
                &format!("skipping Install Rustfmt stage{} ({})", self.compiler.stage, self.target),
            );
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        let tarball = builder.ensure(dist::Analysis {
            // Find the actual compiler (handling the full bootstrap option) which
            // produced the save-analysis data because that data isn't copied
            // through the sysroot uplifting.
            compiler: builder.compiler_for(builder.top_stage, builder.config.build, self.target),
            target: self.target
        }).expect("missing analysis");
        install_sh(builder, "analysis", self.compiler.stage, Some(self.target), &tarball);
    };
    Rustc, "src/librustc", true, only_hosts: true, {
        let tarball = builder.ensure(dist::Rustc {
            compiler: builder.compiler(builder.top_stage, self.target),
        });
        install_sh(builder, "rustc", self.compiler.stage, Some(self.target), &tarball);
    };
);

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Src {
    pub stage: u32,
}

impl Step for Src {
    type Output = ();
    const DEFAULT: bool = true;
    const ONLY_HOSTS: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        let config = &run.builder.config;
        let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src"));
        run.path("src").default_condition(cond)
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Src { stage: run.builder.top_stage });
    }

    fn run(self, builder: &Builder<'_>) {
        let tarball = builder.ensure(dist::Src);
        install_sh(builder, "src", self.stage, None, &tarball);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use criterion::*;
use crate::partition::*;
use crate::partition::BlockSize::*;
use crate::plane::*;
use rand::{ChaChaRng, Rng, SeedableRng};
use rav1e::me;
use rav1e::Pixel;

fn fill_plane<T: Pixel>(ra: &mut ChaChaRng, plane: &mut Plane<T>) {
  let stride = plane.cfg.stride;
  for row in plane.data.chunks_mut(stride) {
    for pixel in row {
      let v: u8 = ra.gen();
      *pixel = T::cast_from(v);
    }
  }
}

fn new_plane<T: Pixel>(ra: &mut ChaChaRng, width: usize, height: usize) -> Plane<T> {
  let mut p = Plane::new(width, height, 0, 0, 128 + 8, 128 + 8);

  fill_plane(ra, &mut p);

  p
}

fn bench_get_sad(b: &mut Bencher, &(bs, bit_depth): &(BlockSize, usize)) {
  let mut ra = ChaChaRng::from_seed([0; 32]);
  let bsw = bs.width();
  let bsh = bs.height();
  let w = 640;
  let h = 480;
  let input_plane = new_plane::<u16>(&mut ra, w, h);
  let rec_plane = new_plane::<u16>(&mut ra, w, h);
  let po = PlaneOffset { x: 0, y: 0 };

  let plane_org = input_plane.slice(po);
  let plane_ref = rec_plane.slice(po);

  b.iter(|| {
    let _ =
      black_box(me::get_sad(&plane_org, &plane_ref, bsh, bsw, bit_depth));
  })
}

pub fn get_sad(c: &mut Criterion) {
  let blocks = vec![
    (BLOCK_4X4, 8),
    (BLOCK_4X8, 8),
    (BLOCK_8X4, 8),
    (BLOCK_8X8, 8),
    (BLOCK_8X16, 8),
    (BLOCK_16X8, 8),
    (BLOCK_16X16, 8),
    (BLOCK_16X32, 8),
    (BLOCK_32X16, 8),
    (BLOCK_32X32, 8),
    (BLOCK_32X64, 8),
    (BLOCK_64X32, 8),
    (BLOCK_64X64, 8),
    (BLOCK_64X128, 8),
    (BLOCK_128X64, 8),
    (BLOCK_128X128, 8),
    (BLOCK_4X16, 8),
    (BLOCK_16X4, 8),
    (BLOCK_8X32, 8),
    (BLOCK_32X8, 8),
    (BLOCK_16X64, 8),
    (BLOCK_64X16, 8),

    (BLOCK_4X4, 10),
    (BLOCK_4X8, 10),
    (BLOCK_8X4, 10),
    (BLOCK_8X8, 10),
    (BLOCK_8X16, 10),
    (BLOCK_16X8, 10),
    (BLOCK_16X16, 10),
    (BLOCK_16X32, 10),
    (BLOCK_32X16, 10),
    (BLOCK_32X32, 10),
    (BLOCK_32X64, 10),
    (BLOCK_64X32, 10),
    (BLOCK_64X64, 10),
    (BLOCK_64X128, 10),
    (BLOCK_128X64, 10),
    (BLOCK_128X128, 10),
    (BLOCK_4X16, 10),
    (BLOCK_16X4, 10),
    (BLOCK_8X32, 10),
    (BLOCK_32X8, 10),
    (BLOCK_16X64, 10),
    (BLOCK_64X16, 10)
  ];

  c.bench_function_over_inputs("get_sad", bench_get_sad, blocks);
}
<commit_msg>SAD bench: use the right Pixel format (u8 or u16) in function of the bit depth<commit_after>// Copyright (c) 2017-2018, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use criterion::*;
use crate::partition::*;
use crate::partition::BlockSize::*;
use crate::plane::*;
use rand::{ChaChaRng, Rng, SeedableRng};
use rav1e::me;
use rav1e::Pixel;

fn fill_plane<T: Pixel>(ra: &mut ChaChaRng, plane: &mut Plane<T>) {
  let stride = plane.cfg.stride;
  for row in plane.data.chunks_mut(stride) {
    for pixel in row {
      let v: u8 = ra.gen();
      *pixel = T::cast_from(v);
    }
  }
}

fn new_plane<T: Pixel>(ra: &mut ChaChaRng, width: usize, height: usize) -> Plane<T> {
  let mut p = Plane::new(width, height, 0, 0, 128 + 8, 128 + 8);

  fill_plane(ra, &mut p);

  p
}

fn run_sad_bench<T: Pixel>(b: &mut Bencher, &(bs, bit_depth): &(BlockSize, usize)) {
  let mut ra = ChaChaRng::from_seed([0; 32]);
  let bsw = bs.width();
  let bsh = bs.height();
  let w = 640;
  let h = 480;
  let input_plane = new_plane::<T>(&mut ra, w, h);
  let rec_plane = new_plane::<T>(&mut ra, w, h);
  let po = PlaneOffset { x: 0, y: 0 };

  let plane_org = input_plane.slice(po);
  let plane_ref = rec_plane.slice(po);

  b.iter(|| {
    let _ =
      black_box(me::get_sad(&plane_org, &plane_ref, bsh, bsw, bit_depth));
  })
}

fn bench_get_sad(b: &mut Bencher, &(bs, bit_depth): &(BlockSize, usize)) {
  if bit_depth <= 8 {
    run_sad_bench::<u8>(b, &(bs, bit_depth))
  }
  else {
    run_sad_bench::<u16>(b, &(bs, bit_depth))
  }
}

pub fn get_sad(c: &mut Criterion) {
  let blocks = vec![
    (BLOCK_4X4, 8),
    (BLOCK_4X8, 8),
    (BLOCK_8X4, 8),
    (BLOCK_8X8, 8),
    (BLOCK_8X16, 8),
    (BLOCK_16X8, 8),
    (BLOCK_16X16, 8),
    (BLOCK_16X32, 8),
    (BLOCK_32X16, 8),
    (BLOCK_32X32, 8),
    (BLOCK_32X64, 8),
    (BLOCK_64X32, 8),
    (BLOCK_64X64, 8),
    (BLOCK_64X128, 8),
    (BLOCK_128X64, 8),
    (BLOCK_128X128, 8),
    (BLOCK_4X16, 8),
    (BLOCK_16X4, 8),
    (BLOCK_8X32, 8),
    (BLOCK_32X8, 8),
    (BLOCK_16X64, 8),
    (BLOCK_64X16, 8),

    (BLOCK_4X4, 10),
    (BLOCK_4X8, 10),
    (BLOCK_8X4, 10),
    (BLOCK_8X8, 10),
    (BLOCK_8X16, 10),
    (BLOCK_16X8, 10),
    (BLOCK_16X16, 10),
    (BLOCK_16X32, 10),
    (BLOCK_32X16, 10),
    (BLOCK_32X32, 10),
    (BLOCK_32X64, 10),
    (BLOCK_64X32, 10),
    (BLOCK_64X64, 10),
    (BLOCK_64X128, 10),
    (BLOCK_128X64, 10),
    (BLOCK_128X128, 10),
    (BLOCK_4X16, 10),
    (BLOCK_16X4, 10),
    (BLOCK_8X32, 10),
    (BLOCK_32X8, 10),
    (BLOCK_16X64, 10),
    (BLOCK_64X16, 10)
  ];

  c.bench_function_over_inputs("get_sad", bench_get_sad, blocks);
}
<|endoftext|>


<commit_before>use std::{cell::RefCell, rc::Rc};

use dces::prelude::*;

use crate::{prelude::*, render::RenderContext2D, theming::Selector, tree::Tree};

/// This system is used to initializes the widgets.
#[derive(Constructor)]
pub struct InitSystem {
    context_provider: ContextProvider,
    registry: Rc<RefCell<Registry>>,
}

impl System<Tree, RenderContext2D> for InitSystem {
    fn run_with_context(
        &self,
        ecm: &mut EntityComponentManager<Tree>,
        render_context: &mut RenderContext2D,
    ) {
        let root = ecm.entity_store().root();

        #[cfg(feature = "debug")]
        let debug = true;
        #[cfg(not(feature = "debug"))]
        let debug = false;

        if debug {
            crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string());

            print_tree(root, 0, ecm);

            crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string());
        }

        // init css ids
        let theme = ecm
            .component_store()
            .get::<Rc<Theme>>("theme", root)
            .unwrap()
            .clone();

        let mut current_node = root;

        loop {
            {
                let mut ctx = Context::new(
                    (current_node, ecm),
                    &theme,
                    &self.context_provider,
                    render_context,
                );

                if let Some(state) = self
                    .context_provider
                    .states
                    .borrow_mut()
                    .get_mut(&current_node)
                {
                    state.init(&mut *self.registry.borrow_mut(), &mut ctx);
                }

                drop(ctx);
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }
    }
}

pub fn print_tree(entity: Entity, depth: usize, ecm: &mut EntityComponentManager<Tree>) {
    let name = ecm.component_store().get::<String>("name", entity).unwrap();

    let selector = if let Ok(selector) = ecm.component_store().get::<Selector>("selector", entity) {
        selector.clone()
    } else {
        Selector::default()
    };

    crate::shell::CONSOLE.log(format!(
        "{}{} (entity: {}{})",
        "| ".repeat(depth),
        name,
        entity.0,
        selector
    ));

    for child in ecm.entity_store().clone().children.get(&entity).unwrap() {
        print_tree(*child, depth + 1, ecm);
    }
}
<commit_msg>orbtk_core: update `init_system`<commit_after>use std::{cell::RefCell, rc::Rc};

use dces::prelude::*;

use crate::{prelude::*, render::RenderContext2D, theming::Selector, tree::Tree};

/// This system is used to initializes the widgets.
#[derive(Constructor)]
pub struct InitSystem {
    context_provider: ContextProvider,
    registry: Rc<RefCell<Registry>>,
}

impl System<Tree, RenderContext2D> for InitSystem {
    fn run_with_context(
        &self,
        ecm: &mut EntityComponentManager<Tree>,
        render_context: &mut RenderContext2D,
    ) {
        let root = ecm.entity_store().root();

        #[cfg(feature = "debug")]
        let debug = true;
        #[cfg(not(feature = "debug"))]
        let debug = false;

        if debug {
            crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string());

            print_tree(root, 0, ecm);

            crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string());
        }

        // init css ids
        let theme = ecm
            .component_store()
            .get::<Rc<Theme>>("theme", root)
            .unwrap()
            .clone();

        let mut current_node = root;

        loop {
            {
                let mut ctx = Context::new(
                    (current_node, ecm),
                    &theme,
                    &self.context_provider,
                    render_context,
                );

                if let Some(state) = self
                    .context_provider
                    .states
                    .borrow_mut()
                    .get_mut(&current_node)
                {
                    state.init(&mut *self.registry.borrow_mut(), &mut ctx);
                }

                drop(ctx);
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }
    }
}

pub fn print_tree(entity: Entity, depth: usize, ecm: &mut EntityComponentManager<Tree>) {
    let name = ecm.component_store().get::<String>("name", entity).unwrap();

    let selector = if let Ok(selector) = ecm.component_store().get::<Selector>("selector", entity) {
        selector.clone()
    } else {
        Selector::default()
    };

    crate::shell::CONSOLE.log(format!(
        "{}{} (entity: {}, {})",
        "| ".repeat(depth),
        name,
        entity.0,
        selector
    ));

    for child in ecm.entity_store().clone().children.get(&entity).unwrap() {
        print_tree(*child, depth + 1, ecm);
    }
}
<|endoftext|>


<commit_before><commit_msg>virtio-net: unittest rx rate limiter blocked case<commit_after><|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate rand;

mod paste_id;

use std::io;
use std::fs::File;
use std::path::Path;

use rocket::Data;
use rocket::response::{content, Failure};
use rocket::http::StatusCode::NotFound;

use paste_id::PasteID;

const HOST: &'static str = "http://localhost:8000";
const ID_LENGTH: usize = 3;

#[post("/", data = "<paste>")]
fn upload(paste: Data) -> io::Result<content::Plain<String>> {
    let id = PasteID::new(ID_LENGTH);
    let filename = format!("upload/{id}", id = id);
    let url = format!("{host}/{id}\n", host = HOST, id = id);

    paste.stream_to_file(Path::new(&filename))?;
    Ok(content::Plain(url))
}

#[get("/<id>")]
fn retrieve(id: PasteID) -> Result<content::Plain<File>, Failure> {
    let filename = format!("upload/{id}", id = id);
    File::open(&filename).map(|f| content::Plain(f)).map_err(|_| Failure(NotFound))
}

#[get("/")]
fn index() -> &'static str {
    "
    USAGE

      POST /

          accepts raw data in the body of the request and responds with a URL of
          a page containing the body's content

          EXMAPLE: curl --data-binary @file.txt http://localhost:8000

      GET /<id>

          retrieves the content for the paste with id `<id>`
    "
}

fn main() {
    rocket::ignite().mount("/", routes![index, upload, retrieve]).launch()
}
<commit_msg>Simplify pastebin retrieve handler.<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate rand;

mod paste_id;

use std::io;
use std::fs::File;
use std::path::Path;

use rocket::Data;
use rocket::response::content;

use paste_id::PasteID;

const HOST: &'static str = "http://localhost:8000";
const ID_LENGTH: usize = 3;

#[post("/", data = "<paste>")]
fn upload(paste: Data) -> io::Result<content::Plain<String>> {
    let id = PasteID::new(ID_LENGTH);
    let filename = format!("upload/{id}", id = id);
    let url = format!("{host}/{id}\n", host = HOST, id = id);

    paste.stream_to_file(Path::new(&filename))?;
    Ok(content::Plain(url))
}

#[get("/<id>")]
fn retrieve(id: PasteID) -> Option<content::Plain<File>> {
    let filename = format!("upload/{id}", id = id);
    File::open(&filename).map(|f| content::Plain(f)).ok()
}

#[get("/")]
fn index() -> &'static str {
    "
    USAGE

      POST /

          accepts raw data in the body of the request and responds with a URL of
          a page containing the body's content

          EXMAPLE: curl --data-binary @file.txt http://localhost:8000

      GET /<id>

          retrieves the content for the paste with id `<id>`
    "
}

fn main() {
    rocket::ignite().mount("/", routes![index, upload, retrieve]).launch()
}
<|endoftext|>


<commit_before>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

use hook::error::HookError as HE;
use hook::error::HookErrorKind as HEK;
use configuration::AspectConfig;

#[derive(Debug)]
pub struct Aspect {
    cfg: Option<AspectConfig>,
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String, cfg: Option<AspectConfig>) -> Aspect {
        Aspect {
            cfg: cfg,
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();

        fn is_file_accessor(a: &HDA) -> bool {
            match a {
                &HDA::MutableAccess(_)  => true,
                &HDA::NonMutableAccess(_)  => true,
                _ => false,
            }
        }

        if !accessors.iter().all(|a| is_file_accessor(a)) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor)    => try!(accessor.access_mut(fle)),

                // TODO: Naiive implementation.
                // More sophisticated version would check whether there are _chunks_ of
                // NonMutableAccess accessors and execute these chunks in parallel. We do not have
                // performance concerns yet, so this is okay.
                HDA::NonMutableAccess(accessor) => try!(accessor.access(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        use crossbeam;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::NonMutableAccess(accessor) => accessor.access(fle),
                            _ => unreachable!(),
                        }
                        .map_err(|e| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

<commit_msg>Remove unused variables<commit_after>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

use hook::error::HookError as HE;
use hook::error::HookErrorKind as HEK;
use configuration::AspectConfig;

#[derive(Debug)]
pub struct Aspect {
    cfg: Option<AspectConfig>,
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String, cfg: Option<AspectConfig>) -> Aspect {
        Aspect {
            cfg: cfg,
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use crossbeam;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::StoreIdAccess(accessor) => accessor.access(id),
                            _ => unreachable!(),
                        }
                        .map_err(|_| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();

        fn is_file_accessor(a: &HDA) -> bool {
            match a {
                &HDA::MutableAccess(_)  => true,
                &HDA::NonMutableAccess(_)  => true,
                _ => false,
            }
        }

        if !accessors.iter().all(|a| is_file_accessor(a)) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor)    => try!(accessor.access_mut(fle)),

                // TODO: Naiive implementation.
                // More sophisticated version would check whether there are _chunks_ of
                // NonMutableAccess accessors and execute these chunks in parallel. We do not have
                // performance concerns yet, so this is okay.
                HDA::NonMutableAccess(accessor) => try!(accessor.access(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        use crossbeam;

        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            return Err(HE::new(HEK::AccessTypeViolation, None));
        }

        let threads : Vec<HookResult<()>> = accessors
            .iter()
            .map(|accessor| {
                crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        match accessor {
                            &HDA::NonMutableAccess(accessor) => accessor.access(fle),
                            _ => unreachable!(),
                        }
                        .map_err(|_| ()) // TODO: We're losing the error cause here
                    })
                })
            })
            .map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
            .collect();

        threads
            .into_iter()
            .fold(Ok(()), |acc, elem| {
                acc.and_then(|a| {
                    elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None))
                })
            })
    }
}

<|endoftext|>