<commit_before>#![doc(html_root_url = "https://saschagrunert.github.io/git-journal/")]
#![deny(missing_docs)]

//! # The Git Commit Message Framework
//!
//! Target of the project is to provide a Rust based framework to write more sensible commit
//! messages. Single commit messages should contain one logical change of the project which is
//! described in a standardized way. This results in a much cleaner git history and provides
//! contributors more information about the actual change.
//!
//! To gain very clean commit message history it is necessary to use git rebase, squashed and
//! amended commits. git-journal will simplify these development approaches by providing sensible
//! conventions and strong defaults.
//!
//! ### Basic usage example
//!
//! ```
//! use git_journal::GitJournal;
//! let mut journal = GitJournal::new(".").unwrap();
//! journal.parse_log("HEAD", "rc", &1, &false, &true);
//! journal.print_log(true).expect("Could not print short log.");
//! ```
//!
//! Simply create a new git-journal struct from a given path (`.` in this example). Then parse the
//! log between a given commit range or a single commit. In this example we want to retrieve
//! everything included in the last git tag, which does not represent a release candidate (contains
//! `"rc"`). After that parsing the log will be printed in the shortest possible format.
//!

extern crate chrono;
extern crate git2;
extern crate regex;
extern crate rustc_serialize;
extern crate term;
extern crate toml;

#[macro_use]
extern crate nom;

#[macro_use]
extern crate lazy_static;

use git2::{ObjectType, Oid, Repository};
use chrono::{UTC, TimeZone};

use parser::{Parser, ParsedCommit, ParsedTag, Print};
use config::Config;

use std::fmt;
use std::fs::File;
use std::io::prelude::*;

#[macro_use]
mod macros;
mod parser;
mod config;

/// An enumeration of possible errors that can happen when working with git-journal.
#[derive(Debug)]
pub enum Error {
    /// Erros related to the git repository.
    Git(git2::Error),

    /// Erros related to the system IO, like parsing of the configuration file.
    Io(std::io::Error),

    /// Errors related to the terminal emulation, which is used for colored output.
    Term(term::Error),

    /// Errors related to the setup process.
    Setup(config::Error),

    /// Errors related to the printing of the log.
    Print(parser::Error),
}

impl From<git2::Error> for Error {
    fn from(err: git2::Error) -> Error {
        Error::Git(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<term::Error> for Error {
    fn from(err: term::Error) -> Error {
        Error::Term(err)
    }
}

impl From<config::Error> for Error {
    fn from(err: config::Error) -> Error {
        Error::Setup(err)
    }
}

impl From<parser::Error> for Error {
    fn from(err: parser::Error) -> Error {
        Error::Print(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Git(ref err) => write!(f, "Git error: {}", err),
            Error::Io(ref err) => write!(f, "Io error: {}", err),
            Error::Term(ref err) => write!(f, "Term error: {}", err),
            Error::Setup(ref err) => write!(f, "Setup error: {}", err),
            Error::Print(ref err) => write!(f, "Print error: {}", err),
        }
    }
}

/// The main structure of git-journal.
pub struct GitJournal {
    repo: Repository,
    tags: Vec<(Oid, String)>,
    parse_result: Vec<(ParsedTag, Vec<ParsedCommit>)>,
    config: Config,
}

impl GitJournal {
    /// Constructs a new `GitJournal<Result<GitJournal, Error>>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let journal = GitJournal::new(".").unwrap();
    /// ```
    ///
    /// # Errors
    /// When not providing a path with a valid git repository ('.git' folder or the initial parsing
    /// of the git tags failed.
    ///
    pub fn new(path: &str) -> Result<GitJournal, Error> {
        // Open the repository
        let new_repo = try!(Repository::open(path));

        // Get all available tags in some vector of tuples
        let mut new_tags = vec![];
        for name in try!(new_repo.tag_names(None)).iter() {
            let name = try!(name.ok_or(git2::Error::from_str("Could not receive tag name")));
            let obj = try!(new_repo.revparse_single(name));
            if let Ok(tag) = obj.into_tag() {
                let tag_name = try!(tag.name().ok_or(git2::Error::from_str("Could not parse tag name"))).to_owned();
                new_tags.push((tag.target_id(), tag_name));
            }
        }

        // Search for config in path and load
        let mut new_config = Config::new();
        match new_config.load(path) {
            Ok(()) => println_ok!("Loaded configuration file from '{}'.", path),
            Err(e) => println_info!("Could not open configuration file, using default values. ({})", e),
        }

        // Return the git journal object
        Ok(GitJournal {
            repo: new_repo,
            tags: new_tags,
            parse_result: vec![],
            config: new_config,
        })
    }

    /// Does the setup on the target git repository.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// GitJournal::setup(".").expect("Setup error");
    /// ```
    ///
    /// Creates a `.gitjournal` file with the default values inside the given path, which looks
    /// like:
    ///
    /// ```toml
    /// # Set to false if the output should not be colored
    /// colored_output = true
    ///
    /// # Excluded tags in an array, e.g. "internal"
    /// excluded_tags = []
    ///
    /// # Show or hide the commit message prefix, e.g. JIRA-1234
    /// show_prefix = false
    /// ```
    ///
    /// # Errors
    /// When the writing of the default configuration fails.
    ///
    pub fn setup(path: &str) -> Result<(), Error> {
        let output_file = try!(Config::new().save_default_config(path));
        println_ok!("Setup complete, defaults written to '{}' file.",
                    output_file);
        Ok(())
    }

    /// Verify a given commit message against the parsing rules of
    /// [RFC0001](https://github.com/saschagrunert/git-journal/blob/master/rfc/0001-commit-msg.md)
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// GitJournal::verify("COMMIT_MSG").expect("Commit message verification error");
    /// ```
    ///
    /// # Errors
    /// When the commit message is not valid due to RFC0001 or opening of the given file failed.
    ///
    pub fn verify(path: &str) -> Result<(), Error> {
        let mut file = try!(File::open(path));
        let mut commit_message = String::new();
        try!(file.read_to_string(&mut commit_message));
        try!(Parser.parse_commit_message(&commit_message));
        println_ok!("Commit message valid.");
        Ok(())
    }

    /// Parses a revision range for a `GitJournal`.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let mut journal = GitJournal::new(".").unwrap();
    /// journal.parse_log("HEAD", "rc", &1, &false, &false);
    /// ```
    ///
    /// # Errors
    /// When something during the parsing fails, for example if the revision range is invalid.
    ///
    pub fn parse_log(&mut self,
                     revision_range: &str,
                     tag_skip_pattern: &str,
                     max_tags_count: &u32,
                     all: &bool,
                     skip_unreleased: &bool)
                     -> Result<(), Error> {

        let mut revwalk = try!(self.repo.revwalk());
        revwalk.set_sorting(git2::SORT_TIME);

        // Fill the revwalk with the selected revisions.
        let revspec = try!(self.repo.revparse(&revision_range));
        if revspec.mode().contains(git2::REVPARSE_SINGLE) {
            // A single commit was given
            let from = try!(revspec.from().ok_or(git2::Error::from_str("Could not set revision range start")));
            try!(revwalk.push(from.id()));
        } else {
            // A specific commit range was given
            let from = try!(revspec.from().ok_or(git2::Error::from_str("Could not set revision range start")));
            let to = try!(revspec.to().ok_or(git2::Error::from_str("Could not set revision range end")));
            try!(revwalk.push(to.id()));
            if revspec.mode().contains(git2::REVPARSE_MERGE_BASE) {
                let base = try!(self.repo.merge_base(from.id(), to.id()));
                let o = try!(self.repo.find_object(base, Some(ObjectType::Commit)));
                try!(revwalk.push(o.id()));
            }
            try!(revwalk.hide(from.id()));
        }

        // Iterate over the git objects and collect them in a vector of tuples
        let mut current_entries: Vec<ParsedCommit> = vec![];
        let mut parsed_tags: u32 = 1;
        let unreleased_str = "Unreleased";
        let mut current_tag = ParsedTag {
            name: unreleased_str.to_owned(),
            date: UTC::today(),
        };
        'revloop: for (index, id) in revwalk.enumerate() {
            let oid = try!(id);
            let commit = try!(self.repo.find_commit(oid));
            for tag in self.tags
                .iter()
                .filter(|tag| tag.0.as_bytes() == oid.as_bytes() && !tag.1.contains(tag_skip_pattern)) {

                // Parsing entries of the last tag done
                if !current_entries.is_empty() {
                    self.parse_result.push((current_tag.clone(), current_entries.clone()));
                    current_entries.clear();
                }

                // If a single revision is given stop at the first seen tag
                if !all && index > 0 && parsed_tags > *max_tags_count {
                    break 'revloop;
                }

                // Format the tag and set as current
                parsed_tags += 1;
                let date = UTC.timestamp(commit.time().seconds(), 0).date();
                current_tag = ParsedTag {
                    name: tag.1.clone(),
                    date: date,
                };
            }

            // Do not parse if we want to skip commits which do not belong to any release
            if *skip_unreleased && current_tag.name == unreleased_str {
                continue;
            }

            // Add the commit message to the current entries of the tag
            let message = try!(commit.message().ok_or(git2::Error::from_str("Parsing error:")));

            match Parser.parse_commit_message(message) {
                Ok(parsed_message) => current_entries.push(parsed_message),
                Err(e) => println_info!("Skipping commit: {}", e),
            }
        }
        // Add the last processed items as well
        if !current_entries.is_empty() {
            self.parse_result.push((current_tag, current_entries));
        }

        Ok(())
    }

    /// Prints the resulting log in a short or detailed variant.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let mut journal = GitJournal::new(".").unwrap();
    /// journal.parse_log("HEAD", "rc", &1, &false, &false);
    /// journal.print_log(true).expect("Could not print short log.");
    /// journal.print_log(false).expect("Could not print detailed log.");
    /// ```
    ///
    /// # Errors
    /// If some commit message could not be print.
    ///
    pub fn print_log(&self, only_short: bool) -> Result<(), Error> {
        for &(ref tag, ref commits) in &self.parse_result {
            try!(tag.print(&self.config));
            let mut c = commits.clone();
            c.sort_by(|a, b| a.summary.category.cmp(&b.summary.category));
            for commit in c {
                if only_short {
                    try!(commit.summary.print(&self.config));
                } else {
                    try!(commit.print(&self.config));
                }
            }
        }
        Ok(())
    }
}
<commit_msg>Changed commit message test to actually work<commit_after>#![doc(html_root_url = "https://saschagrunert.github.io/git-journal/")]
#![deny(missing_docs)]

//! # The Git Commit Message Framework
//!
//! Target of the project is to provide a Rust based framework to write more sensible commit
//! messages. Single commit messages should contain one logical change of the project which is
//! described in a standardized way. This results in a much cleaner git history and provides
//! contributors more information about the actual change.
//!
//! To gain very clean commit message history it is necessary to use git rebase, squashed and
//! amended commits. git-journal will simplify these development approaches by providing sensible
//! conventions and strong defaults.
//!
//! ### Basic usage example
//!
//! ```
//! use git_journal::GitJournal;
//! let mut journal = GitJournal::new(".").unwrap();
//! journal.parse_log("HEAD", "rc", &1, &false, &true);
//! journal.print_log(true).expect("Could not print short log.");
//! ```
//!
//! Simply create a new git-journal struct from a given path (`.` in this example). Then parse the
//! log between a given commit range or a single commit. In this example we want to retrieve
//! everything included in the last git tag, which does not represent a release candidate (contains
//! `"rc"`). After that parsing the log will be printed in the shortest possible format.
//!

extern crate chrono;
extern crate git2;
extern crate regex;
extern crate rustc_serialize;
extern crate term;
extern crate toml;

#[macro_use]
extern crate nom;

#[macro_use]
extern crate lazy_static;

use git2::{ObjectType, Oid, Repository};
use chrono::{UTC, TimeZone};

use parser::{Parser, ParsedCommit, ParsedTag, Print};
use config::Config;

use std::fmt;
use std::fs::File;
use std::io::prelude::*;

#[macro_use]
mod macros;
mod parser;
mod config;

/// An enumeration of possible errors that can happen when working with git-journal.
#[derive(Debug)]
pub enum Error {
    /// Erros related to the git repository.
    Git(git2::Error),

    /// Erros related to the system IO, like parsing of the configuration file.
    Io(std::io::Error),

    /// Errors related to the terminal emulation, which is used for colored output.
    Term(term::Error),

    /// Errors related to the setup process.
    Setup(config::Error),

    /// Errors related to the printing of the log.
    Print(parser::Error),
}

impl From<git2::Error> for Error {
    fn from(err: git2::Error) -> Error {
        Error::Git(err)
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<term::Error> for Error {
    fn from(err: term::Error) -> Error {
        Error::Term(err)
    }
}

impl From<config::Error> for Error {
    fn from(err: config::Error) -> Error {
        Error::Setup(err)
    }
}

impl From<parser::Error> for Error {
    fn from(err: parser::Error) -> Error {
        Error::Print(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Git(ref err) => write!(f, "Git error: {}", err),
            Error::Io(ref err) => write!(f, "Io error: {}", err),
            Error::Term(ref err) => write!(f, "Term error: {}", err),
            Error::Setup(ref err) => write!(f, "Setup error: {}", err),
            Error::Print(ref err) => write!(f, "Print error: {}", err),
        }
    }
}

/// The main structure of git-journal.
pub struct GitJournal {
    repo: Repository,
    tags: Vec<(Oid, String)>,
    parse_result: Vec<(ParsedTag, Vec<ParsedCommit>)>,
    config: Config,
}

impl GitJournal {
    /// Constructs a new `GitJournal<Result<GitJournal, Error>>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let journal = GitJournal::new(".").unwrap();
    /// ```
    ///
    /// # Errors
    /// When not providing a path with a valid git repository ('.git' folder or the initial parsing
    /// of the git tags failed.
    ///
    pub fn new(path: &str) -> Result<GitJournal, Error> {
        // Open the repository
        let new_repo = try!(Repository::open(path));

        // Get all available tags in some vector of tuples
        let mut new_tags = vec![];
        for name in try!(new_repo.tag_names(None)).iter() {
            let name = try!(name.ok_or(git2::Error::from_str("Could not receive tag name")));
            let obj = try!(new_repo.revparse_single(name));
            if let Ok(tag) = obj.into_tag() {
                let tag_name = try!(tag.name().ok_or(git2::Error::from_str("Could not parse tag name"))).to_owned();
                new_tags.push((tag.target_id(), tag_name));
            }
        }

        // Search for config in path and load
        let mut new_config = Config::new();
        match new_config.load(path) {
            Ok(()) => println_ok!("Loaded configuration file from '{}'.", path),
            Err(e) => println_info!("Could not open configuration file, using default values. ({})", e),
        }

        // Return the git journal object
        Ok(GitJournal {
            repo: new_repo,
            tags: new_tags,
            parse_result: vec![],
            config: new_config,
        })
    }

    /// Does the setup on the target git repository.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// GitJournal::setup(".").expect("Setup error");
    /// ```
    ///
    /// Creates a `.gitjournal` file with the default values inside the given path, which looks
    /// like:
    ///
    /// ```toml
    /// # Set to false if the output should not be colored
    /// colored_output = true
    ///
    /// # Excluded tags in an array, e.g. "internal"
    /// excluded_tags = []
    ///
    /// # Show or hide the commit message prefix, e.g. JIRA-1234
    /// show_prefix = false
    /// ```
    ///
    /// # Errors
    /// When the writing of the default configuration fails.
    ///
    pub fn setup(path: &str) -> Result<(), Error> {
        let output_file = try!(Config::new().save_default_config(path));
        println_ok!("Setup complete, defaults written to '{}' file.",
                    output_file);
        Ok(())
    }

    /// Verify a given commit message against the parsing rules of
    /// [RFC0001](https://github.com/saschagrunert/git-journal/blob/master/rfc/0001-commit-msg.md)
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// GitJournal::verify(".git/COMMIT_EDITMSG").expect("Commit message verification error");
    /// ```
    ///
    /// # Errors
    /// When the commit message is not valid due to RFC0001 or opening of the given file failed.
    ///
    pub fn verify(path: &str) -> Result<(), Error> {
        let mut file = try!(File::open(path));
        let mut commit_message = String::new();
        try!(file.read_to_string(&mut commit_message));
        try!(Parser.parse_commit_message(&commit_message));
        println_ok!("Commit message valid.");
        Ok(())
    }

    /// Parses a revision range for a `GitJournal`.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let mut journal = GitJournal::new(".").unwrap();
    /// journal.parse_log("HEAD", "rc", &1, &false, &false);
    /// ```
    ///
    /// # Errors
    /// When something during the parsing fails, for example if the revision range is invalid.
    ///
    pub fn parse_log(&mut self,
                     revision_range: &str,
                     tag_skip_pattern: &str,
                     max_tags_count: &u32,
                     all: &bool,
                     skip_unreleased: &bool)
                     -> Result<(), Error> {

        let mut revwalk = try!(self.repo.revwalk());
        revwalk.set_sorting(git2::SORT_TIME);

        // Fill the revwalk with the selected revisions.
        let revspec = try!(self.repo.revparse(&revision_range));
        if revspec.mode().contains(git2::REVPARSE_SINGLE) {
            // A single commit was given
            let from = try!(revspec.from().ok_or(git2::Error::from_str("Could not set revision range start")));
            try!(revwalk.push(from.id()));
        } else {
            // A specific commit range was given
            let from = try!(revspec.from().ok_or(git2::Error::from_str("Could not set revision range start")));
            let to = try!(revspec.to().ok_or(git2::Error::from_str("Could not set revision range end")));
            try!(revwalk.push(to.id()));
            if revspec.mode().contains(git2::REVPARSE_MERGE_BASE) {
                let base = try!(self.repo.merge_base(from.id(), to.id()));
                let o = try!(self.repo.find_object(base, Some(ObjectType::Commit)));
                try!(revwalk.push(o.id()));
            }
            try!(revwalk.hide(from.id()));
        }

        // Iterate over the git objects and collect them in a vector of tuples
        let mut current_entries: Vec<ParsedCommit> = vec![];
        let mut parsed_tags: u32 = 1;
        let unreleased_str = "Unreleased";
        let mut current_tag = ParsedTag {
            name: unreleased_str.to_owned(),
            date: UTC::today(),
        };
        'revloop: for (index, id) in revwalk.enumerate() {
            let oid = try!(id);
            let commit = try!(self.repo.find_commit(oid));
            for tag in self.tags
                .iter()
                .filter(|tag| tag.0.as_bytes() == oid.as_bytes() && !tag.1.contains(tag_skip_pattern)) {

                // Parsing entries of the last tag done
                if !current_entries.is_empty() {
                    self.parse_result.push((current_tag.clone(), current_entries.clone()));
                    current_entries.clear();
                }

                // If a single revision is given stop at the first seen tag
                if !all && index > 0 && parsed_tags > *max_tags_count {
                    break 'revloop;
                }

                // Format the tag and set as current
                parsed_tags += 1;
                let date = UTC.timestamp(commit.time().seconds(), 0).date();
                current_tag = ParsedTag {
                    name: tag.1.clone(),
                    date: date,
                };
            }

            // Do not parse if we want to skip commits which do not belong to any release
            if *skip_unreleased && current_tag.name == unreleased_str {
                continue;
            }

            // Add the commit message to the current entries of the tag
            let message = try!(commit.message().ok_or(git2::Error::from_str("Parsing error:")));

            match Parser.parse_commit_message(message) {
                Ok(parsed_message) => current_entries.push(parsed_message),
                Err(e) => println_info!("Skipping commit: {}", e),
            }
        }
        // Add the last processed items as well
        if !current_entries.is_empty() {
            self.parse_result.push((current_tag, current_entries));
        }

        Ok(())
    }

    /// Prints the resulting log in a short or detailed variant.
    ///
    /// # Examples
    ///
    /// ```
    /// use git_journal::GitJournal;
    ///
    /// let mut journal = GitJournal::new(".").unwrap();
    /// journal.parse_log("HEAD", "rc", &1, &false, &false);
    /// journal.print_log(true).expect("Could not print short log.");
    /// journal.print_log(false).expect("Could not print detailed log.");
    /// ```
    ///
    /// # Errors
    /// If some commit message could not be print.
    ///
    pub fn print_log(&self, only_short: bool) -> Result<(), Error> {
        for &(ref tag, ref commits) in &self.parse_result {
            try!(tag.print(&self.config));
            let mut c = commits.clone();
            c.sort_by(|a, b| a.summary.category.cmp(&b.summary.category));
            for commit in c {
                if only_short {
                    try!(commit.summary.print(&self.config));
                } else {
                    try!(commit.print(&self.config));
                }
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate hyper;
extern crate tokio_core;

use std::str::FromStr;
use tokio_core::reactor::{Handle};

use futures::Future;
use hyper::{Method, Request, Uri, Client};
use hyper::header::{Authorization, ContentLength, ContentType};

use librespot::playback::player::PlayerEvent;

#[derive(Clone)]
pub struct LMS {
	base_url: Option<String>,
	player_mac: Option<String>,
	auth: Option<String>
}

#[allow(unused)]
impl LMS {
	pub fn new(base_url: Option<String>, player_mac: Option<String>, auth: Option<String>) -> LMS {
		LMS {
			base_url: Some(format!("http://{}/jsonrpc.js", base_url.unwrap_or("localhost:9000".to_string()))),
			player_mac: player_mac,
			auth: auth
		}
	}

	pub fn is_configured(&self) -> bool {
		if self.base_url != None {
			if self.player_mac != None {
				return true;
			}
		}

		return false;
	}

	pub fn signal_event(&self, event: PlayerEvent, handle: Handle) {
		let mut command = r#"["spottyconnect","change"]"#.to_string();

		match event {
			PlayerEvent::Changed {
				old_track_id,
				new_track_id,
			} => {
				#[cfg(debug_assertions)]
				info!("change: spotify:track:{} -> spotify:track:{}", old_track_id.to_base62(), new_track_id.to_base62());
				command = r#"["spottyconnect","change"]"#.to_string();
			}
			PlayerEvent::Started { track_id } => {
				#[cfg(debug_assertions)]
				info!("play spotify:track:{}", track_id.to_base62());
				command = r#"["spottyconnect","start"]"#.to_string();
			}
			PlayerEvent::Stopped { track_id } => {
				#[cfg(debug_assertions)]
				info!("stop spotify:track:{}", track_id.to_base62());
				command = r#"["spottyconnect","stop"]"#.to_string();
			}
			PlayerEvent::Volume { volume } => {
				#[cfg(debug_assertions)]
				info!("volume {}", volume);
				// we're not using the volume here, as LMS will read player state anyway
				command = format!(r#"["spottyconnect","volume",{}]"#, volume.to_string());
			}
			PlayerEvent::Seek { position } => {
				#[cfg(debug_assertions)]
				info!("seek {}", position);
				// we're not implementing the seek event here, as it's going to read player state anyway
				command = r#"["spottyconnect","change"]"#.to_string();
			}
		}

		if !self.is_configured() {
			#[cfg(debug_assertions)]
			info!("LMS connection is not configured");
			return;
		}

		#[cfg(debug_assertions)]
		info!("Base URL to talk to LMS: {}", self.base_url.clone().unwrap());

		if let Some(ref base_url) = self.base_url {
			#[cfg(debug_assertions)]
			info!("Player MAC address to control: {}", self.player_mac.clone().unwrap());
			if let Some(ref player_mac) = self.player_mac {

				let client = Client::new(&handle);

				#[cfg(debug_assertions)]
				info!("Command to send to player: {}", command);

				let json = format!(r#"{{"id": 1,"method":"slim.request","params":["{}",{}]}}"#, player_mac, command);
				let uri = Uri::from_str(base_url).unwrap();
				let mut req = Request::new(Method::Post, uri);

				if let Some(ref auth) = self.auth {
					req.headers_mut().set(Authorization(format!("Basic {}", auth).to_owned()));
				}

				req.headers_mut().set_raw("X-Scanner", "1");
				req.headers_mut().set(ContentType::json());
				req.headers_mut().set(ContentLength(json.len() as u64));
				req.set_body(json);

				// ugh... just send that thing and don't care about the rest...
				let post = client.request(req).map(|_| ()).map_err(|_| ());
				handle.spawn(post);
			}
		}
	}
}
<commit_msg>Send LMS the track ID. Unfortunately I don't know how to send the full URI. So this must do for now...<commit_after>extern crate futures;
extern crate hyper;
extern crate tokio_core;

use std::str::FromStr;
use tokio_core::reactor::{Handle};

use futures::Future;
use hyper::{Method, Request, Uri, Client};
use hyper::header::{Authorization, ContentLength, ContentType};

use librespot::playback::player::PlayerEvent;

#[derive(Clone)]
pub struct LMS {
	base_url: Option<String>,
	player_mac: Option<String>,
	auth: Option<String>
}

#[allow(unused)]
impl LMS {
	pub fn new(base_url: Option<String>, player_mac: Option<String>, auth: Option<String>) -> LMS {
		LMS {
			base_url: Some(format!("http://{}/jsonrpc.js", base_url.unwrap_or("localhost:9000".to_string()))),
			player_mac: player_mac,
			auth: auth
		}
	}

	pub fn is_configured(&self) -> bool {
		if self.base_url != None {
			if self.player_mac != None {
				return true;
			}
		}

		return false;
	}

	pub fn signal_event(&self, event: PlayerEvent, handle: Handle) {
		let mut command = r#"["spottyconnect","change"]"#.to_string();

		match event {
			PlayerEvent::Changed {
				old_track_id,
				new_track_id,
			} => {
				#[cfg(debug_assertions)]
				info!("change: spotify:track:{} -> spotify:track:{}", old_track_id.to_base62(), new_track_id.to_base62());
				command = format!(r#"["spottyconnect","change","{}","{}"]"#, new_track_id.to_base62().to_string(), old_track_id.to_base62().to_string());
			}
			PlayerEvent::Started { track_id } => {
				#[cfg(debug_assertions)]
				info!("play spotify:track:{}", track_id.to_base62());
				command = format!(r#"["spottyconnect","start","{}"]"#, track_id.to_base62().to_string());
			}
			PlayerEvent::Stopped { track_id } => {
				#[cfg(debug_assertions)]
				info!("stop spotify:track:{}", track_id.to_base62());
				command = r#"["spottyconnect","stop"]"#.to_string();
			}
			PlayerEvent::Volume { volume } => {
				#[cfg(debug_assertions)]
				info!("volume {}", volume);
				// we're not using the volume here, as LMS will read player state anyway
				command = format!(r#"["spottyconnect","volume",{}]"#, volume.to_string());
			}
			PlayerEvent::Seek { position } => {
				#[cfg(debug_assertions)]
				info!("seek {}", position);
				// we're not implementing the seek event here, as it's going to read player state anyway
				command = r#"["spottyconnect","change"]"#.to_string();
			}
		}

		if !self.is_configured() {
			#[cfg(debug_assertions)]
			info!("LMS connection is not configured");
			return;
		}

		#[cfg(debug_assertions)]
		info!("Base URL to talk to LMS: {}", self.base_url.clone().unwrap());

		if let Some(ref base_url) = self.base_url {
			#[cfg(debug_assertions)]
			info!("Player MAC address to control: {}", self.player_mac.clone().unwrap());
			if let Some(ref player_mac) = self.player_mac {

				let client = Client::new(&handle);

				#[cfg(debug_assertions)]
				info!("Command to send to player: {}", command);

				let json = format!(r#"{{"id": 1,"method":"slim.request","params":["{}",{}]}}"#, player_mac, command);
				let uri = Uri::from_str(base_url).unwrap();
				let mut req = Request::new(Method::Post, uri);

				if let Some(ref auth) = self.auth {
					req.headers_mut().set(Authorization(format!("Basic {}", auth).to_owned()));
				}

				req.headers_mut().set_raw("X-Scanner", "1");
				req.headers_mut().set(ContentType::json());
				req.headers_mut().set(ContentLength(json.len() as u64));
				req.set_body(json);

				// ugh... just send that thing and don't care about the rest...
				let post = client.request(req).map(|_| ()).map_err(|_| ());
				handle.spawn(post);
			}
		}
	}
}
<|endoftext|>


<commit_before>//! # Tracer
//!
//! The OpenTelemetry library achieves in-process context propagation of
//! `Span`s by way of the `Tracer`.
//!
//! The `Tracer` is responsible for tracking the currently active `Span`,
//! and exposes methods for creating and activating new `Spans`.
//!
//! Docs: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#tracer
use crate::sdk::{
    trace::{
        provider::{TracerProvider, TracerProviderInner},
        span::{Span, SpanData},
        EvictedHashMap, EvictedQueue, SamplingDecision, SamplingResult,
    },
    InstrumentationLibrary,
};
use crate::trace::{
    Link, SpanBuilder, SpanContext, SpanId, SpanKind, StatusCode, TraceContextExt, TraceId,
    TraceState, TRACE_FLAG_SAMPLED,
};
use crate::{Context, KeyValue};
use std::fmt;
use std::sync::Weak;

/// `Tracer` implementation to create and manage spans
#[derive(Clone)]
pub struct Tracer {
    instrumentation_lib: InstrumentationLibrary,
    provider: Weak<TracerProviderInner>,
}

impl fmt::Debug for Tracer {
    /// Formats the `Tracer` using the given formatter.
    /// Omitting `provider` here is necessary to avoid cycles.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Tracer")
            .field("name", &self.instrumentation_lib.name)
            .field("version", &self.instrumentation_lib.version)
            .finish()
    }
}

impl Tracer {
    /// Create a new tracer (used internally by `TracerProvider`s).
    pub(crate) fn new(
        instrumentation_lib: InstrumentationLibrary,
        provider: Weak<TracerProviderInner>,
    ) -> Self {
        Tracer {
            instrumentation_lib,
            provider,
        }
    }

    /// TracerProvider associated with this tracer.
    pub fn provider(&self) -> Option<TracerProvider> {
        self.provider.upgrade().map(TracerProvider::new)
    }

    /// instrumentation library information of this tracer.
    pub fn instrumentation_library(&self) -> &InstrumentationLibrary {
        &self.instrumentation_lib
    }

    /// Make a sampling decision using the provided sampler for the span and context.
    #[allow(clippy::too_many_arguments)]
    fn make_sampling_decision(
        &self,
        parent_cx: &Context,
        trace_id: TraceId,
        name: &str,
        span_kind: &SpanKind,
        attributes: &[KeyValue],
        links: &[Link],
    ) -> Option<(u8, Vec<KeyValue>, TraceState)> {
        let provider = self.provider()?;
        let sampler = &provider.config().default_sampler;

        let sampling_result = sampler.should_sample(
            Some(parent_cx),
            trace_id,
            name,
            span_kind,
            attributes,
            links,
        );

        self.process_sampling_result(sampling_result, parent_cx)
    }

    fn process_sampling_result(
        &self,
        sampling_result: SamplingResult,
        parent_cx: &Context,
    ) -> Option<(u8, Vec<KeyValue>, TraceState)> {
        match sampling_result {
            SamplingResult {
                decision: SamplingDecision::Drop,
                ..
            } => None,
            SamplingResult {
                decision: SamplingDecision::RecordOnly,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags & !TRACE_FLAG_SAMPLED, attributes, trace_state))
            }
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags | TRACE_FLAG_SAMPLED, attributes, trace_state))
            }
        }
    }
}

impl crate::trace::Tracer for Tracer {
    /// This implementation of `Tracer` produces `sdk::Span` instances.
    type Span = Span;

    /// Returns a span with an inactive `SpanContext`. Used by functions that
    /// need to return a default span like `get_active_span` if no span is present.
    fn invalid(&self) -> Self::Span {
        Span::new(SpanContext::empty_context(), None, self.clone())
    }

    /// Starts a new `Span` with a given context.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn start_with_context(&self, name: &str, cx: Context) -> Self::Span {
        let mut builder = self.span_builder(name);
        builder.parent_context = Some(cx);

        self.build(builder)
    }

    /// Creates a span builder
    ///
    /// An ergonomic way for attributes to be configured before the `Span` is started.
    fn span_builder(&self, name: &str) -> SpanBuilder {
        SpanBuilder::from_name(name.to_string())
    }

    /// Starts a span from a `SpanBuilder`.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn build(&self, mut builder: SpanBuilder) -> Self::Span {
        let provider = self.provider();
        if provider.is_none() {
            return Span::new(SpanContext::empty_context(), None, self.clone());
        }

        let provider = provider.unwrap();
        let config = provider.config();
        let span_id = builder
            .span_id
            .take()
            .unwrap_or_else(|| config.id_generator.new_span_id());

        let span_kind = builder.span_kind.take().unwrap_or(SpanKind::Internal);
        let mut attribute_options = builder.attributes.take().unwrap_or_else(Vec::new);
        let mut link_options = builder.links.take();
        let mut flags = 0;
        let mut span_trace_state = Default::default();

        let parent_cx = builder
            .parent_context
            .take()
            .map(|cx| {
                // Sampling expects to be able to access the parent span via `span` so wrap remote span
                // context in a wrapper span if necessary. Remote span contexts will be passed to
                // subsequent context's, so wrapping is only necessary if there is no active span.
                match cx.remote_span_context() {
                    Some(remote_sc) if !cx.has_active_span() => {
                        cx.with_span(Span::new(remote_sc.clone(), None, self.clone()))
                    }
                    _ => cx,
                }
            })
            .unwrap_or_else(Context::current);
        let parent_span_context = if parent_cx.has_active_span() {
            Some(parent_cx.span().span_context())
        } else {
            None
        };
        // Build context for sampling decision
        let (no_parent, trace_id, parent_span_id, remote_parent, parent_trace_flags) =
            parent_span_context
                .as_ref()
                .map(|ctx| {
                    (
                        false,
                        ctx.trace_id(),
                        ctx.span_id(),
                        ctx.is_remote(),
                        ctx.trace_flags(),
                    )
                })
                .unwrap_or((
                    true,
                    builder
                        .trace_id
                        .unwrap_or_else(|| config.id_generator.new_trace_id()),
                    SpanId::invalid(),
                    false,
                    0,
                ));

        // There are 3 paths for sampling.
        //
        // * Sampling has occurred elsewhere and is already stored in the builder
        // * There is no parent or a remote parent, in which case make decision now
        // * There is a local parent, in which case defer to the parent's decision
        let sampling_decision = if let Some(sampling_result) = builder.sampling_result.take() {
            self.process_sampling_result(sampling_result, &parent_cx)
        } else if no_parent || remote_parent {
            self.make_sampling_decision(
                &parent_cx,
                trace_id,
                &builder.name,
                &span_kind,
                &attribute_options,
                link_options.as_deref().unwrap_or(&[]),
            )
        } else {
            // has parent that is local: use parent if sampled, or don't record.
            parent_span_context
                .filter(|span_context| span_context.is_sampled())
                .map(|span_context| {
                    (
                        parent_trace_flags,
                        Vec::new(),
                        span_context.trace_state().clone(),
                    )
                })
        };

        // Build optional inner context, `None` if not recording.
        let inner = sampling_decision.map(|(trace_flags, mut extra_attrs, trace_state)| {
            flags = trace_flags;
            span_trace_state = trace_state;
            attribute_options.append(&mut extra_attrs);
            let mut attributes =
                EvictedHashMap::new(config.max_attributes_per_span, attribute_options.len());
            for attribute in attribute_options {
                attributes.insert(attribute);
            }
            let mut links = EvictedQueue::new(config.max_links_per_span);
            if let Some(link_options) = &mut link_options {
                links.append_vec(link_options);
            }
            let start_time = builder.start_time.unwrap_or_else(crate::time::now);
            let end_time = builder.end_time.unwrap_or(start_time);
            let mut message_events = EvictedQueue::new(config.max_events_per_span);
            if let Some(mut events) = builder.message_events {
                message_events.append_vec(&mut events);
            }
            let status_code = builder.status_code.unwrap_or(StatusCode::Unset);
            let status_message = builder.status_message.unwrap_or_else(String::new);
            let resource = config.resource.clone();

            SpanData {
                parent_span_id,
                span_kind,
                name: builder.name,
                start_time,
                end_time,
                attributes,
                message_events,
                links,
                status_code,
                status_message,
                resource,
            }
        });

        let span_context = SpanContext::new(trace_id, span_id, flags, false, span_trace_state);
        let span = Span::new(span_context, inner, self.clone());

        // Call `on_start` for all processors
        for processor in provider.span_processors() {
            processor.on_start(&span, &parent_cx)
        }

        span
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        sdk::{
            self,
            trace::{Config, Sampler, SamplingDecision, SamplingResult, ShouldSample},
        },
        testing::trace::TestSpan,
        trace::{
            Link, Span, SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceId,
            TraceState, Tracer, TracerProvider, TRACE_FLAG_SAMPLED,
        },
        Context, KeyValue,
    };

    #[derive(Debug)]
    struct TestSampler {}

    impl ShouldSample for TestSampler {
        fn should_sample(
            &self,
            parent_context: Option<&Context>,
            _trace_id: TraceId,
            _name: &str,
            _span_kind: &SpanKind,
            _attributes: &[KeyValue],
            _links: &[Link],
        ) -> SamplingResult {
            let trace_state = parent_context
                .unwrap()
                .span()
                .span_context()
                .trace_state()
                .clone();
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes: Vec::new(),
                trace_state: trace_state.insert("foo".into(), "notbar".into()).unwrap(),
            }
        }
    }

    #[test]
    fn allow_sampler_to_change_trace_state() {
        // Setup
        let sampler = TestSampler {};
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.get_tracer("test", None);
        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
        let mut span_builder = SpanBuilder::default();
        span_builder.parent_context = Some(Context::current_with_span(TestSpan(SpanContext::new(
            TraceId::from_u128(128),
            SpanId::from_u64(64),
            TRACE_FLAG_SAMPLED,
            true,
            trace_state,
        ))));

        // Test sampler should change trace state
        let span = tracer.build(span_builder);
        let span_context = span.span_context();
        let expected = span_context.trace_state();
        assert_eq!(expected.get("foo"), Some("notbar"))
    }

    #[test]
    fn drop_parent_based_children() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();

        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
        let tracer = tracer_provider.get_tracer("test", None);
        let span = tracer.start_with_context("must_not_be_sampled", context);

        assert!(!span.span_context().is_sampled());
    }

    #[test]
    fn uses_current_context_for_builders_if_unset() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();

        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
        let tracer = tracer_provider.get_tracer("test", None);
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);

        assert!(!span.span_context().is_sampled());
    }
}
<commit_msg>Fix remote implicit builder context sampling (#405)<commit_after>//! # Tracer
//!
//! The OpenTelemetry library achieves in-process context propagation of
//! `Span`s by way of the `Tracer`.
//!
//! The `Tracer` is responsible for tracking the currently active `Span`,
//! and exposes methods for creating and activating new `Spans`.
//!
//! Docs: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#tracer
use crate::sdk::{
    trace::{
        provider::{TracerProvider, TracerProviderInner},
        span::{Span, SpanData},
        EvictedHashMap, EvictedQueue, SamplingDecision, SamplingResult,
    },
    InstrumentationLibrary,
};
use crate::trace::{
    Link, SpanBuilder, SpanContext, SpanId, SpanKind, StatusCode, TraceContextExt, TraceId,
    TraceState, TRACE_FLAG_SAMPLED,
};
use crate::{Context, KeyValue};
use std::fmt;
use std::sync::Weak;

/// `Tracer` implementation to create and manage spans
#[derive(Clone)]
pub struct Tracer {
    instrumentation_lib: InstrumentationLibrary,
    provider: Weak<TracerProviderInner>,
}

impl fmt::Debug for Tracer {
    /// Formats the `Tracer` using the given formatter.
    /// Omitting `provider` here is necessary to avoid cycles.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Tracer")
            .field("name", &self.instrumentation_lib.name)
            .field("version", &self.instrumentation_lib.version)
            .finish()
    }
}

impl Tracer {
    /// Create a new tracer (used internally by `TracerProvider`s).
    pub(crate) fn new(
        instrumentation_lib: InstrumentationLibrary,
        provider: Weak<TracerProviderInner>,
    ) -> Self {
        Tracer {
            instrumentation_lib,
            provider,
        }
    }

    /// TracerProvider associated with this tracer.
    pub fn provider(&self) -> Option<TracerProvider> {
        self.provider.upgrade().map(TracerProvider::new)
    }

    /// instrumentation library information of this tracer.
    pub fn instrumentation_library(&self) -> &InstrumentationLibrary {
        &self.instrumentation_lib
    }

    /// Make a sampling decision using the provided sampler for the span and context.
    #[allow(clippy::too_many_arguments)]
    fn make_sampling_decision(
        &self,
        parent_cx: &Context,
        trace_id: TraceId,
        name: &str,
        span_kind: &SpanKind,
        attributes: &[KeyValue],
        links: &[Link],
    ) -> Option<(u8, Vec<KeyValue>, TraceState)> {
        let provider = self.provider()?;
        let sampler = &provider.config().default_sampler;

        let sampling_result = sampler.should_sample(
            Some(parent_cx),
            trace_id,
            name,
            span_kind,
            attributes,
            links,
        );

        self.process_sampling_result(sampling_result, parent_cx)
    }

    fn process_sampling_result(
        &self,
        sampling_result: SamplingResult,
        parent_cx: &Context,
    ) -> Option<(u8, Vec<KeyValue>, TraceState)> {
        match sampling_result {
            SamplingResult {
                decision: SamplingDecision::Drop,
                ..
            } => None,
            SamplingResult {
                decision: SamplingDecision::RecordOnly,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags & !TRACE_FLAG_SAMPLED, attributes, trace_state))
            }
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes,
                trace_state,
            } => {
                let trace_flags = parent_cx.span().span_context().trace_flags();
                Some((trace_flags | TRACE_FLAG_SAMPLED, attributes, trace_state))
            }
        }
    }
}

impl crate::trace::Tracer for Tracer {
    /// This implementation of `Tracer` produces `sdk::Span` instances.
    type Span = Span;

    /// Returns a span with an inactive `SpanContext`. Used by functions that
    /// need to return a default span like `get_active_span` if no span is present.
    fn invalid(&self) -> Self::Span {
        Span::new(SpanContext::empty_context(), None, self.clone())
    }

    /// Starts a new `Span` with a given context.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn start_with_context(&self, name: &str, cx: Context) -> Self::Span {
        let mut builder = self.span_builder(name);
        builder.parent_context = Some(cx);

        self.build(builder)
    }

    /// Creates a span builder
    ///
    /// An ergonomic way for attributes to be configured before the `Span` is started.
    fn span_builder(&self, name: &str) -> SpanBuilder {
        SpanBuilder::from_name(name.to_string())
    }

    /// Starts a span from a `SpanBuilder`.
    ///
    /// Each span has zero or one parent spans and zero or more child spans, which
    /// represent causally related operations. A tree of related spans comprises a
    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
    /// trace includes a single root span, which is the shared ancestor of all other
    /// spans in the trace.
    fn build(&self, mut builder: SpanBuilder) -> Self::Span {
        let provider = self.provider();
        if provider.is_none() {
            return Span::new(SpanContext::empty_context(), None, self.clone());
        }

        let provider = provider.unwrap();
        let config = provider.config();
        let span_id = builder
            .span_id
            .take()
            .unwrap_or_else(|| config.id_generator.new_span_id());

        let span_kind = builder.span_kind.take().unwrap_or(SpanKind::Internal);
        let mut attribute_options = builder.attributes.take().unwrap_or_else(Vec::new);
        let mut link_options = builder.links.take();
        let mut flags = 0;
        let mut span_trace_state = Default::default();

        let parent_cx = {
            let cx = builder
                .parent_context
                .take()
                .unwrap_or_else(Context::current);

            // Sampling expects to be able to access the parent span via `span` so wrap remote span
            // context in a wrapper span if necessary. Remote span contexts will be passed to
            // subsequent context's, so wrapping is only necessary if there is no active span.
            match cx.remote_span_context() {
                Some(remote_sc) if !cx.has_active_span() => {
                    cx.with_span(Span::new(remote_sc.clone(), None, self.clone()))
                }
                _ => cx,
            }
        };
        let parent_span_context = if parent_cx.has_active_span() {
            Some(parent_cx.span().span_context())
        } else {
            None
        };
        // Build context for sampling decision
        let (no_parent, trace_id, parent_span_id, remote_parent, parent_trace_flags) =
            parent_span_context
                .as_ref()
                .map(|ctx| {
                    (
                        false,
                        ctx.trace_id(),
                        ctx.span_id(),
                        ctx.is_remote(),
                        ctx.trace_flags(),
                    )
                })
                .unwrap_or((
                    true,
                    builder
                        .trace_id
                        .unwrap_or_else(|| config.id_generator.new_trace_id()),
                    SpanId::invalid(),
                    false,
                    0,
                ));

        // There are 3 paths for sampling.
        //
        // * Sampling has occurred elsewhere and is already stored in the builder
        // * There is no parent or a remote parent, in which case make decision now
        // * There is a local parent, in which case defer to the parent's decision
        let sampling_decision = if let Some(sampling_result) = builder.sampling_result.take() {
            self.process_sampling_result(sampling_result, &parent_cx)
        } else if no_parent || remote_parent {
            self.make_sampling_decision(
                &parent_cx,
                trace_id,
                &builder.name,
                &span_kind,
                &attribute_options,
                link_options.as_deref().unwrap_or(&[]),
            )
        } else {
            // has parent that is local: use parent if sampled, or don't record.
            parent_span_context
                .filter(|span_context| span_context.is_sampled())
                .map(|span_context| {
                    (
                        parent_trace_flags,
                        Vec::new(),
                        span_context.trace_state().clone(),
                    )
                })
        };

        // Build optional inner context, `None` if not recording.
        let inner = sampling_decision.map(|(trace_flags, mut extra_attrs, trace_state)| {
            flags = trace_flags;
            span_trace_state = trace_state;
            attribute_options.append(&mut extra_attrs);
            let mut attributes =
                EvictedHashMap::new(config.max_attributes_per_span, attribute_options.len());
            for attribute in attribute_options {
                attributes.insert(attribute);
            }
            let mut links = EvictedQueue::new(config.max_links_per_span);
            if let Some(link_options) = &mut link_options {
                links.append_vec(link_options);
            }
            let start_time = builder.start_time.unwrap_or_else(crate::time::now);
            let end_time = builder.end_time.unwrap_or(start_time);
            let mut message_events = EvictedQueue::new(config.max_events_per_span);
            if let Some(mut events) = builder.message_events {
                message_events.append_vec(&mut events);
            }
            let status_code = builder.status_code.unwrap_or(StatusCode::Unset);
            let status_message = builder.status_message.unwrap_or_else(String::new);
            let resource = config.resource.clone();

            SpanData {
                parent_span_id,
                span_kind,
                name: builder.name,
                start_time,
                end_time,
                attributes,
                message_events,
                links,
                status_code,
                status_message,
                resource,
            }
        });

        let span_context = SpanContext::new(trace_id, span_id, flags, false, span_trace_state);
        let span = Span::new(span_context, inner, self.clone());

        // Call `on_start` for all processors
        for processor in provider.span_processors() {
            processor.on_start(&span, &parent_cx)
        }

        span
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        sdk::{
            self,
            trace::{Config, Sampler, SamplingDecision, SamplingResult, ShouldSample},
        },
        testing::trace::TestSpan,
        trace::{
            Link, Span, SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceId,
            TraceState, Tracer, TracerProvider, TRACE_FLAG_NOT_SAMPLED, TRACE_FLAG_SAMPLED,
        },
        Context, KeyValue,
    };

    #[derive(Debug)]
    struct TestSampler {}

    impl ShouldSample for TestSampler {
        fn should_sample(
            &self,
            parent_context: Option<&Context>,
            _trace_id: TraceId,
            _name: &str,
            _span_kind: &SpanKind,
            _attributes: &[KeyValue],
            _links: &[Link],
        ) -> SamplingResult {
            let trace_state = parent_context
                .unwrap()
                .span()
                .span_context()
                .trace_state()
                .clone();
            SamplingResult {
                decision: SamplingDecision::RecordAndSample,
                attributes: Vec::new(),
                trace_state: trace_state.insert("foo".into(), "notbar".into()).unwrap(),
            }
        }
    }

    #[test]
    fn allow_sampler_to_change_trace_state() {
        // Setup
        let sampler = TestSampler {};
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.get_tracer("test", None);
        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
        let mut span_builder = SpanBuilder::default();
        span_builder.parent_context = Some(Context::current_with_span(TestSpan(SpanContext::new(
            TraceId::from_u128(128),
            SpanId::from_u64(64),
            TRACE_FLAG_SAMPLED,
            true,
            trace_state,
        ))));

        // Test sampler should change trace state
        let span = tracer.build(span_builder);
        let span_context = span.span_context();
        let expected = span_context.trace_state();
        assert_eq!(expected.get("foo"), Some("notbar"))
    }

    #[test]
    fn drop_parent_based_children() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();

        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
        let tracer = tracer_provider.get_tracer("test", None);
        let span = tracer.start_with_context("must_not_be_sampled", context);

        assert!(!span.span_context().is_sampled());
    }

    #[test]
    fn uses_current_context_for_builders_if_unset() {
        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
        let config = Config::default().with_default_sampler(sampler);
        let tracer_provider = sdk::trace::TracerProvider::builder()
            .with_config(config)
            .build();
        let tracer = tracer_provider.get_tracer("test", None);

        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
        assert!(!span.span_context().is_sampled());

        let _attached = Context::current()
            .with_remote_span_context(SpanContext::new(
                TraceId::from_u128(1),
                SpanId::from_u64(1),
                TRACE_FLAG_NOT_SAMPLED,
                true,
                Default::default(),
            ))
            .attach();
        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);

        assert!(!span.span_context().is_sampled());
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]
use alloc::heap::{Heap, Alloc, Layout};
use bytes::BytesMut;

use std::rc::Rc;

use audiosample::*;
use pixel::*;
use timeinfo::*;

error_chain! {
    errors {
        InvalidIndex
    }
}

#[derive(Clone,Debug,PartialEq)]
pub struct VideoInfo {
    pub width: usize,
    pub height: usize,
    pub format: Rc<Formaton>,
}

impl VideoInfo {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

#[derive(Clone,Debug,PartialEq)]
pub struct AudioInfo {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Rc<Soniton>,
}

impl AudioInfo {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum MediaKind {
    Video(VideoInfo),
    Audio(AudioInfo),
}

use self::MediaKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> Result<usize>;
    fn count(&self) -> usize;
}

pub struct Frame {
    pub kind: MediaKind,
    pub buf: Box<FrameBuffer>,
    pub t: Option<TimeInfo>,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: BytesMut,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: BytesMut,
    planes: Vec<Plane>,
}

impl FrameBuffer for DefaultFrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&plane.buf)
        }
    }
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]> {
        match self.planes.get_mut(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&mut plane.buf)
        }
    }
    fn linesize(&self, idx: usize) -> Result<usize> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(plane.linesize)
        }
    }
    fn count(&self) -> usize {
        self.planes.len()
    }
}

impl DefaultFrameBuffer {
    pub fn new(kind: &MediaKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe { Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap()).unwrap() };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = BytesMut::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}

pub fn new_default_frame(kind: &MediaKind, t: Option<TimeInfo>) -> Frame {
    let buf = DefaultFrameBuffer::new(kind);

    Frame { kind: kind.clone(), buf: box buf, t: t }
}
<commit_msg>frame: rustfmt<commit_after>#![allow(dead_code, unused_variables)]
use alloc::heap::{Heap, Alloc, Layout};
use bytes::BytesMut;

use std::rc::Rc;

use audiosample::*;
use pixel::*;
use timeinfo::*;

error_chain! {
    errors {
        InvalidIndex
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct VideoInfo {
    pub width: usize,
    pub height: usize,
    pub format: Rc<Formaton>,
}

impl VideoInfo {
    fn size(&self, align: usize) -> usize {
        let mut size = 0;
        for &component in self.format.into_iter() {
            if let Some(c) = component {
                size += c.get_data_size(self.width, self.height, align);
            }
        }
        size
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct AudioInfo {
    pub samples: usize,
    pub rate: usize,
    pub map: ChannelMap,
    pub format: Rc<Soniton>,
}

impl AudioInfo {
    fn size(&self, align: usize) -> usize {
        unimplemented!()
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MediaKind {
    Video(VideoInfo),
    Audio(AudioInfo),
}

use self::MediaKind::*;

pub trait FrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]>;
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]>;
    fn linesize(&self, idx: usize) -> Result<usize>;
    fn count(&self) -> usize;
}

pub struct Frame {
    pub kind: MediaKind,
    pub buf: Box<FrameBuffer>,
    pub t: Option<TimeInfo>,
}

const ALIGNMENT: usize = 32;

struct Plane {
    buf: BytesMut,
    linesize: usize,
}

struct DefaultFrameBuffer {
    buf: BytesMut,
    planes: Vec<Plane>,
}

impl FrameBuffer for DefaultFrameBuffer {
    fn as_slice<'a>(&'a self, idx: usize) -> Result<&'a [u8]> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&plane.buf),
        }
    }
    fn as_mut_slice<'a>(&'a mut self, idx: usize) -> Result<&'a mut [u8]> {
        match self.planes.get_mut(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(&mut plane.buf),
        }
    }
    fn linesize(&self, idx: usize) -> Result<usize> {
        match self.planes.get(idx) {
            None => Err(Error::from_kind(ErrorKind::InvalidIndex)),
            Some(plane) => Ok(plane.linesize),
        }
    }
    fn count(&self) -> usize {
        self.planes.len()
    }
}

impl DefaultFrameBuffer {
    pub fn new(kind: &MediaKind) -> DefaultFrameBuffer {
        match kind {
            &Video(ref video) => {
                let size = video.size(ALIGNMENT);
                let data = unsafe {
                    Heap.alloc(Layout::from_size_align(size, ALIGNMENT).unwrap())
                        .unwrap()
                };
                //let data = unsafe { Heap.alloc_zeroed(Layout::from_size_align(size, ALIGNMENT)) };
                let buf = BytesMut::from(unsafe { Vec::from_raw_parts(data, size, size) });
                let mut buffer = DefaultFrameBuffer {
                    buf: buf,
                    planes: Vec::new(),
                };
                for &component in video.format.iter() {
                    if let Some(c) = component {
                        let planesize = c.get_data_size(video.width, video.height, ALIGNMENT);
                        let linesize = c.get_linesize(video.width, ALIGNMENT);
                        buffer.planes.push(Plane {
                            buf: buffer.buf.split_to(planesize),
                            linesize: linesize,
                        });
                    }
                }
                buffer
            }
            _ => unimplemented!(),
        }
    }
}

pub fn new_default_frame(kind: &MediaKind, t: Option<TimeInfo>) -> Frame {
    let buf = DefaultFrameBuffer::new(kind);

    Frame {
        kind: kind.clone(),
        buf: box buf,
        t: t,
    }
}
<|endoftext|>


<commit_before>use crate::interface::{Compiler, Result};
use crate::passes::{self, BoxedResolver, ExpansionResult, BoxedGlobalCtxt, PluginInfo};

use rustc_incremental::DepGraphFuture;
use rustc::session::config::{OutputFilenames, OutputType};
use rustc::util::common::{time, ErrorReported};
use rustc::hir;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::ty::steal::Steal;
use rustc::dep_graph::DepGraph;
use std::cell::{Ref, RefMut, RefCell};
use std::rc::Rc;
use std::sync::mpsc;
use std::any::Any;
use std::mem;
use syntax::{self, ast};

/// Represent the result of a query.
/// This result can be stolen with the `take` method and returned with the `give` method.
pub struct Query<T> {
    result: RefCell<Option<Result<T>>>,
}

impl<T> Query<T> {
    fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
        let mut result = self.result.borrow_mut();
        if result.is_none() {
            *result = Some(f());
        }
        result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
    }

    /// Takes ownership of the query result. Further attempts to take or peek the query
    /// result will panic unless it is returned by calling the `give` method.
    pub fn take(&self) -> T {
        self.result
            .borrow_mut()
            .take()
            .expect("missing query result")
            .unwrap()
    }

    /// Returns a stolen query result. Panics if there's already a result.
    pub fn give(&self, value: T) {
        let mut result = self.result.borrow_mut();
        assert!(result.is_none(), "a result already exists");
        *result = Some(Ok(value));
    }

    /// Borrows the query result using the RefCell. Panics if the result is stolen.
    pub fn peek(&self) -> Ref<'_, T> {
        Ref::map(self.result.borrow(), |r| {
            r.as_ref().unwrap().as_ref().expect("missing query result")
        })
    }

    /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
    pub fn peek_mut(&self) -> RefMut<'_, T> {
        RefMut::map(self.result.borrow_mut(), |r| {
            r.as_mut().unwrap().as_mut().expect("missing query result")
        })
    }
}

impl<T> Default for Query<T> {
    fn default() -> Self {
        Query {
            result: RefCell::new(None),
        }
    }
}

#[derive(Default)]
pub(crate) struct Queries {
    dep_graph_future: Query<Option<DepGraphFuture>>,
    parse: Query<ast::Crate>,
    crate_name: Query<String>,
    register_plugins: Query<(ast::Crate, PluginInfo)>,
    expansion: Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>)>,
    dep_graph: Query<DepGraph>,
    lower_to_hir: Query<(Steal<hir::map::Forest>, ExpansionResult)>,
    prepare_outputs: Query<OutputFilenames>,
    codegen_channel: Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
                            Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>,
    global_ctxt: Query<BoxedGlobalCtxt>,
    ongoing_codegen: Query<Box<dyn Any>>,
    link: Query<()>,
}

impl Compiler {
    pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
        self.queries.dep_graph_future.compute(|| {
            Ok(if self.session().opts.build_dep_graph() {
                Some(rustc_incremental::load_dep_graph(self.session()))
            } else {
                None
            })
        })
    }

    pub fn parse(&self) -> Result<&Query<ast::Crate>> {
        self.queries.parse.compute(|| {
            passes::parse(self.session(), &self.input).map_err(
                |mut parse_error| {
                    parse_error.emit();
                    ErrorReported
                },
            )
        })
    }

    pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, PluginInfo)>> {
        self.queries.register_plugins.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let krate = self.parse()?.take();

            passes::register_plugins(
                self,
                self.session(),
                self.cstore(),
                krate,
                &crate_name,
            )
        })
    }

    pub fn crate_name(&self) -> Result<&Query<String>> {
        self.queries.crate_name.compute(|| {
            let parse_result = self.parse()?;
            let krate = parse_result.peek();
            let result = match self.crate_name {
                Some(ref crate_name) => crate_name.clone(),
                None => rustc_codegen_utils::link::find_crate_name(
                    Some(self.session()),
                    &krate.attrs,
                    &self.input
                ),
            };
            Ok(result)
        })
    }

    pub fn expansion(
        &self
    ) -> Result<&Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>)>> {
        self.queries.expansion.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let (krate, plugin_info) = self.register_plugins()?.take();
            passes::configure_and_expand(
                self.sess.clone(),
                self.cstore().clone(),
                krate,
                &crate_name,
                plugin_info,
            ).map(|(krate, resolver)| (krate, Steal::new(Rc::new(RefCell::new(resolver)))))
        })
    }

    pub fn dep_graph(&self) -> Result<&Query<DepGraph>> {
        self.queries.dep_graph.compute(|| {
            Ok(match self.dep_graph_future()?.take() {
                None => DepGraph::new_disabled(),
                Some(future) => {
                    let (prev_graph, prev_work_products) =
                        time(self.session(), "blocked while dep-graph loading finishes", || {
                            future.open().unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
                                message: format!("could not decode incremental cache: {:?}", e),
                            }).open(self.session())
                        });
                    DepGraph::new(prev_graph, prev_work_products)
                }
            })
        })
    }

    pub fn lower_to_hir(&self) -> Result<&Query<(Steal<hir::map::Forest>, ExpansionResult)>> {
        self.queries.lower_to_hir.compute(|| {
            let expansion_result = self.expansion()?;
            let peeked = expansion_result.peek();
            let krate = &peeked.0;
            let resolver = peeked.1.steal();
            let hir = Steal::new(resolver.borrow_mut().access(|resolver| {
                passes::lower_to_hir(
                    self.session(),
                    self.cstore(),
                    resolver,
                    &*self.dep_graph()?.peek(),
                    &krate
                )
            })?);
            Ok((hir, BoxedResolver::to_expansion_result(resolver)))
        })
    }

    pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
        self.queries.prepare_outputs.compute(|| {
            self.lower_to_hir()?;
            let krate = self.expansion()?;
            let krate = krate.peek();
            let crate_name = self.crate_name()?;
            let crate_name = crate_name.peek();
            passes::prepare_outputs(self.session(), self, &krate.0, &*crate_name)
        })
    }

    pub fn codegen_channel(&self) -> Result<&Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
                                                    Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>> {
        self.queries.codegen_channel.compute(|| {
            let (tx, rx) = mpsc::channel();
            Ok((Steal::new(tx), Steal::new(rx)))
        })
    }

    pub fn global_ctxt(&self) -> Result<&Query<BoxedGlobalCtxt>> {
        self.queries.global_ctxt.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let outputs = self.prepare_outputs()?.peek().clone();
            let hir = self.lower_to_hir()?;
            let hir = hir.peek();
            let (ref hir_forest, ref expansion) = *hir;
            let tx = self.codegen_channel()?.peek().0.steal();
            Ok(passes::create_global_ctxt(
                self,
                hir_forest.steal(),
                expansion.defs.steal(),
                expansion.resolutions.steal(),
                outputs,
                tx,
                &crate_name))
        })
    }

    pub fn ongoing_codegen(&self) -> Result<&Query<Box<dyn Any>>> {
        self.queries.ongoing_codegen.compute(|| {
            let rx = self.codegen_channel()?.peek().1.steal();
            let outputs = self.prepare_outputs()?;
            self.global_ctxt()?.peek_mut().enter(|tcx| {
                tcx.analysis(LOCAL_CRATE).ok();

                // Don't do code generation if there were any errors
                self.session().compile_status()?;

                Ok(passes::start_codegen(
                    &***self.codegen_backend(),
                    tcx,
                    rx,
                    &*outputs.peek()
                ))
            })
        })
    }

    pub fn link(&self) -> Result<&Query<()>> {
        self.queries.link.compute(|| {
            let sess = self.session();

            let ongoing_codegen = self.ongoing_codegen()?.take();

            self.codegen_backend().join_codegen_and_link(
                ongoing_codegen,
                sess,
                &*self.dep_graph()?.peek(),
                &*self.prepare_outputs()?.peek(),
            ).map_err(|_| ErrorReported)?;

            Ok(())
        })
    }

    pub fn compile(&self) -> Result<()> {
        self.prepare_outputs()?;

        if self.session().opts.output_types.contains_key(&OutputType::DepInfo)
            && self.session().opts.output_types.len() == 1
        {
            return Ok(())
        }

        self.global_ctxt()?;

        // Drop AST after creating GlobalCtxt to free memory
        mem::drop(self.expansion()?.take());

        self.ongoing_codegen()?;

        // Drop GlobalCtxt after starting codegen to free memory
        mem::drop(self.global_ctxt()?.take());

        self.link().map(|_| ())
    }
}
<commit_msg>Don't call `self.parse()` in `Compiler::crate_name()` unless necessary.<commit_after>use crate::interface::{Compiler, Result};
use crate::passes::{self, BoxedResolver, ExpansionResult, BoxedGlobalCtxt, PluginInfo};

use rustc_incremental::DepGraphFuture;
use rustc::session::config::{OutputFilenames, OutputType};
use rustc::util::common::{time, ErrorReported};
use rustc::hir;
use rustc::hir::def_id::LOCAL_CRATE;
use rustc::ty::steal::Steal;
use rustc::dep_graph::DepGraph;
use std::cell::{Ref, RefMut, RefCell};
use std::rc::Rc;
use std::sync::mpsc;
use std::any::Any;
use std::mem;
use syntax::{self, ast};

/// Represent the result of a query.
/// This result can be stolen with the `take` method and returned with the `give` method.
pub struct Query<T> {
    result: RefCell<Option<Result<T>>>,
}

impl<T> Query<T> {
    fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> {
        let mut result = self.result.borrow_mut();
        if result.is_none() {
            *result = Some(f());
        }
        result.as_ref().unwrap().as_ref().map(|_| self).map_err(|err| *err)
    }

    /// Takes ownership of the query result. Further attempts to take or peek the query
    /// result will panic unless it is returned by calling the `give` method.
    pub fn take(&self) -> T {
        self.result
            .borrow_mut()
            .take()
            .expect("missing query result")
            .unwrap()
    }

    /// Returns a stolen query result. Panics if there's already a result.
    pub fn give(&self, value: T) {
        let mut result = self.result.borrow_mut();
        assert!(result.is_none(), "a result already exists");
        *result = Some(Ok(value));
    }

    /// Borrows the query result using the RefCell. Panics if the result is stolen.
    pub fn peek(&self) -> Ref<'_, T> {
        Ref::map(self.result.borrow(), |r| {
            r.as_ref().unwrap().as_ref().expect("missing query result")
        })
    }

    /// Mutably borrows the query result using the RefCell. Panics if the result is stolen.
    pub fn peek_mut(&self) -> RefMut<'_, T> {
        RefMut::map(self.result.borrow_mut(), |r| {
            r.as_mut().unwrap().as_mut().expect("missing query result")
        })
    }
}

impl<T> Default for Query<T> {
    fn default() -> Self {
        Query {
            result: RefCell::new(None),
        }
    }
}

#[derive(Default)]
pub(crate) struct Queries {
    dep_graph_future: Query<Option<DepGraphFuture>>,
    parse: Query<ast::Crate>,
    crate_name: Query<String>,
    register_plugins: Query<(ast::Crate, PluginInfo)>,
    expansion: Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>)>,
    dep_graph: Query<DepGraph>,
    lower_to_hir: Query<(Steal<hir::map::Forest>, ExpansionResult)>,
    prepare_outputs: Query<OutputFilenames>,
    codegen_channel: Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
                            Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>,
    global_ctxt: Query<BoxedGlobalCtxt>,
    ongoing_codegen: Query<Box<dyn Any>>,
    link: Query<()>,
}

impl Compiler {
    pub fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> {
        self.queries.dep_graph_future.compute(|| {
            Ok(if self.session().opts.build_dep_graph() {
                Some(rustc_incremental::load_dep_graph(self.session()))
            } else {
                None
            })
        })
    }

    pub fn parse(&self) -> Result<&Query<ast::Crate>> {
        self.queries.parse.compute(|| {
            passes::parse(self.session(), &self.input).map_err(
                |mut parse_error| {
                    parse_error.emit();
                    ErrorReported
                },
            )
        })
    }

    pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, PluginInfo)>> {
        self.queries.register_plugins.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let krate = self.parse()?.take();

            passes::register_plugins(
                self,
                self.session(),
                self.cstore(),
                krate,
                &crate_name,
            )
        })
    }

    pub fn crate_name(&self) -> Result<&Query<String>> {
        self.queries.crate_name.compute(|| {
            Ok(match self.crate_name {
                Some(ref crate_name) => crate_name.clone(),
                None => {
                    let parse_result = self.parse()?;
                    let krate = parse_result.peek();
                    rustc_codegen_utils::link::find_crate_name(
                        Some(self.session()),
                        &krate.attrs,
                        &self.input
                    )
                }
            })
        })
    }

    pub fn expansion(
        &self
    ) -> Result<&Query<(ast::Crate, Steal<Rc<RefCell<BoxedResolver>>>)>> {
        self.queries.expansion.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let (krate, plugin_info) = self.register_plugins()?.take();
            passes::configure_and_expand(
                self.sess.clone(),
                self.cstore().clone(),
                krate,
                &crate_name,
                plugin_info,
            ).map(|(krate, resolver)| (krate, Steal::new(Rc::new(RefCell::new(resolver)))))
        })
    }

    pub fn dep_graph(&self) -> Result<&Query<DepGraph>> {
        self.queries.dep_graph.compute(|| {
            Ok(match self.dep_graph_future()?.take() {
                None => DepGraph::new_disabled(),
                Some(future) => {
                    let (prev_graph, prev_work_products) =
                        time(self.session(), "blocked while dep-graph loading finishes", || {
                            future.open().unwrap_or_else(|e| rustc_incremental::LoadResult::Error {
                                message: format!("could not decode incremental cache: {:?}", e),
                            }).open(self.session())
                        });
                    DepGraph::new(prev_graph, prev_work_products)
                }
            })
        })
    }

    pub fn lower_to_hir(&self) -> Result<&Query<(Steal<hir::map::Forest>, ExpansionResult)>> {
        self.queries.lower_to_hir.compute(|| {
            let expansion_result = self.expansion()?;
            let peeked = expansion_result.peek();
            let krate = &peeked.0;
            let resolver = peeked.1.steal();
            let hir = Steal::new(resolver.borrow_mut().access(|resolver| {
                passes::lower_to_hir(
                    self.session(),
                    self.cstore(),
                    resolver,
                    &*self.dep_graph()?.peek(),
                    &krate
                )
            })?);
            Ok((hir, BoxedResolver::to_expansion_result(resolver)))
        })
    }

    pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> {
        self.queries.prepare_outputs.compute(|| {
            self.lower_to_hir()?;
            let krate = self.expansion()?;
            let krate = krate.peek();
            let crate_name = self.crate_name()?;
            let crate_name = crate_name.peek();
            passes::prepare_outputs(self.session(), self, &krate.0, &*crate_name)
        })
    }

    pub fn codegen_channel(&self) -> Result<&Query<(Steal<mpsc::Sender<Box<dyn Any + Send>>>,
                                                    Steal<mpsc::Receiver<Box<dyn Any + Send>>>)>> {
        self.queries.codegen_channel.compute(|| {
            let (tx, rx) = mpsc::channel();
            Ok((Steal::new(tx), Steal::new(rx)))
        })
    }

    pub fn global_ctxt(&self) -> Result<&Query<BoxedGlobalCtxt>> {
        self.queries.global_ctxt.compute(|| {
            let crate_name = self.crate_name()?.peek().clone();
            let outputs = self.prepare_outputs()?.peek().clone();
            let hir = self.lower_to_hir()?;
            let hir = hir.peek();
            let (ref hir_forest, ref expansion) = *hir;
            let tx = self.codegen_channel()?.peek().0.steal();
            Ok(passes::create_global_ctxt(
                self,
                hir_forest.steal(),
                expansion.defs.steal(),
                expansion.resolutions.steal(),
                outputs,
                tx,
                &crate_name))
        })
    }

    pub fn ongoing_codegen(&self) -> Result<&Query<Box<dyn Any>>> {
        self.queries.ongoing_codegen.compute(|| {
            let rx = self.codegen_channel()?.peek().1.steal();
            let outputs = self.prepare_outputs()?;
            self.global_ctxt()?.peek_mut().enter(|tcx| {
                tcx.analysis(LOCAL_CRATE).ok();

                // Don't do code generation if there were any errors
                self.session().compile_status()?;

                Ok(passes::start_codegen(
                    &***self.codegen_backend(),
                    tcx,
                    rx,
                    &*outputs.peek()
                ))
            })
        })
    }

    pub fn link(&self) -> Result<&Query<()>> {
        self.queries.link.compute(|| {
            let sess = self.session();

            let ongoing_codegen = self.ongoing_codegen()?.take();

            self.codegen_backend().join_codegen_and_link(
                ongoing_codegen,
                sess,
                &*self.dep_graph()?.peek(),
                &*self.prepare_outputs()?.peek(),
            ).map_err(|_| ErrorReported)?;

            Ok(())
        })
    }

    pub fn compile(&self) -> Result<()> {
        self.prepare_outputs()?;

        if self.session().opts.output_types.contains_key(&OutputType::DepInfo)
            && self.session().opts.output_types.len() == 1
        {
            return Ok(())
        }

        self.global_ctxt()?;

        // Drop AST after creating GlobalCtxt to free memory
        mem::drop(self.expansion()?.take());

        self.ongoing_codegen()?;

        // Drop GlobalCtxt after starting codegen to free memory
        mem::drop(self.global_ctxt()?.take());

        self.link().map(|_| ())
    }
}
<|endoftext|>