<commit_before>use crate::{Change, Index};
use git_repository as git;
use git_repository::prelude::ObjectIdExt;
use git_repository::refs::transaction::PreviousValue;
use std::convert::TryFrom;
use std::sync::atomic::AtomicBool;

mod delegate;
use delegate::Delegate;

/// The error returned by methods dealing with obtaining index changes.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
    #[error("Failed to fetch crates.io index repository")]
    FetchGit2(#[from] git2::Error),
    #[error("Couldn't update marker reference")]
    ReferenceEdit(#[from] git::reference::edit::Error),
    #[error("Failed to parse rev-spec to determine which revisions to diff")]
    RevParse(#[from] git::revision::spec::parse::Error),
    #[error("Couldn't find blob that showed up when diffing trees")]
    FindObject(#[from] git::object::find::existing::Error),
    #[error("Couldn't get the tree of a commit for diffing purposes")]
    PeelToTree(#[from] git::object::peel::to_kind::Error),
    #[error("Failed to diff two trees to find changed crates")]
    Diff(#[from] git::object::tree::diff::Error),
    #[error("Failed to decode {line:?} in file {file_name:?} as crate version")]
    VersionDecode {
        source: serde_json::Error,
        file_name: bstr::BString,
        line: bstr::BString,
    },
    #[error(transparent)]
    FindRemote(#[from] git::remote::find::existing::Error),
    #[error(transparent)]
    FindReference(#[from] git::reference::find::existing::Error),
    #[error(transparent)]
    Connect(#[from] git::remote::connect::Error),
    #[error(transparent)]
    PrepareFetch(#[from] git::remote::fetch::prepare::Error),
    #[error(transparent)]
    Fetch(#[from] git::remote::fetch::Error),
    #[error(transparent)]
    InitAnonymousRemote(#[from] git::remote::init::Error),
    #[error("Could not find local tracking branch for remote branch {name:?} in any of {} fetched refs", mappings.len())]
    NoMatchingBranch {
        name: String,
        mappings: Vec<git::remote::fetch::Mapping>,
    },
}

/// Find changes without modifying the underling repository
impl Index {
    /// As `peek_changes_with_options`, but without the options.
    pub fn peek_changes(&self) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        self.peek_changes_with_options(None)
    }

    /// Return all `Change`s that are observed between the last time `fetch_changes(…)` was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will not be created or updated.
    /// The second field in the returned tuple is the commit object to which the changes were provided.
    /// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
    /// as if `fetch_changes(…)` had been called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    pub fn peek_changes_with_options(
        &self,
        options: Option<&mut git2::FetchOptions<'_>>,
    ) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        let repo = &self.repo;
        let from = repo
            .find_reference(self.seen_ref_name)
            .ok()
            .and_then(|r| r.try_id().map(|id| id.detach()))
            .unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
        let remote_name = self
            .remote_name
            .expect("always set for this old portion of the code");
        let to = {
            let repo = git2::Repository::open(repo.git_dir())?;
            repo.find_remote(remote_name).and_then(|mut r| {
                r.fetch(
                    &[format!(
                        "+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
                        remote = remote_name,
                        branch = self.branch_name,
                    )],
                    options,
                    None,
                )
            })?;
            git::hash::ObjectId::try_from(
                repo.refname_to_id(&format!(
                    "refs/remotes/{}/{}",
                    remote_name, self.branch_name
                ))?
                .as_bytes(),
            )
            .expect("valid oid")
        };

        Ok((self.changes_between_commits(from, to)?, to))
    }

    /// Return all `Change`s that are observed between the last time `peek_changes*(…)` was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin` or whatever is configured for the current `HEAD` branch and lastly
    /// what it should be based on knowledge about he crates index.
    /// The `last_seen_reference()` will not be created or updated.
    /// The second field in the returned tuple is the commit object to which the changes were provided.
    /// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
    /// as if `fetch_changes(…)` had been called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    // TODO: update this once it's clear how auto-gc works in `gitoxide`.
    pub fn peek_changes_with_options2(
        &self,
        progress: impl git::Progress,
        should_interrupt: &AtomicBool,
    ) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        let repo = &self.repo;
        let from = repo
            .find_reference(self.seen_ref_name)
            .ok()
            .and_then(|r| r.try_id().map(|id| id.detach()))
            .unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
        let to = {
            let mut remote = self
                .remote_name
                .and_then(|name| {
                    self.repo.find_remote(name).ok().or_else(|| {
                        self.repo
                            .head()
                            .ok()
                            .and_then(|head| {
                                head.into_remote(git::remote::Direction::Fetch)
                                    .map(|r| r.ok())
                                    .flatten()
                            })
                            .or_else(|| {
                                self.repo
                                    .find_default_remote(git::remote::Direction::Fetch)
                                    .map(|r| r.ok())
                                    .flatten()
                            })
                    })
                })
                .map(Ok)
                .unwrap_or_else(|| {
                    self.repo
                        .head()?
                        .into_remote(git::remote::Direction::Fetch)
                        .map(|r| r.map_err(Error::from))
                        .or_else(|| {
                            self.repo
                                .find_default_remote(git::remote::Direction::Fetch)
                                .map(|r| r.map_err(Error::from))
                        })
                        .unwrap_or_else(|| {
                            self.repo
                                .remote_at("https://github.com/rust-lang/crates.io-index")
                                .map_err(Into::into)
                        })
                })?;
            if remote.refspecs(git::remote::Direction::Fetch).is_empty() {
                let spec = format!(
                    "+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
                    remote = self.remote_name.unwrap_or("origin"),
                    branch = self.branch_name,
                );
                remote
                    .replace_refspecs(Some(spec.as_str()), git::remote::Direction::Fetch)
                    .expect("valid statically known refspec");
            }
            let res: git::remote::fetch::Outcome<'_> = remote
                .connect(git::remote::Direction::Fetch, progress)?
                .prepare_fetch(Default::default())?
                .receive(should_interrupt)?;
            let branch_name = format!("refs/heads/{}", self.branch_name);
            let local_tracking = res
                .ref_map
                .mappings
                .iter()
                .find_map(|m| match &m.remote {
                    git::remote::fetch::Source::Ref(r) => (r.unpack().0 == branch_name)
                        .then(|| m.local.as_ref())
                        .flatten(),
                    _ => None,
                })
                .ok_or_else(|| Error::NoMatchingBranch {
                    name: branch_name,
                    mappings: res.ref_map.mappings.clone(),
                })?;
            self.repo
                .find_reference(local_tracking)
                .expect("local tracking branch exists if we see it here")
                .id()
                .detach()
        };

        Ok((self.changes_between_commits(from, to)?, to))
    }

    /// Similar to `changes()`, but requires `from` and `to` objects to be provided. They may point
    /// to either `Commit`s or `Tree`s.
    pub fn changes_between_commits(
        &self,
        from: impl Into<git::hash::ObjectId>,
        to: impl Into<git::hash::ObjectId>,
    ) -> Result<Vec<Change>, Error> {
        let into_tree = |id: git::hash::ObjectId| -> Result<git::Tree<'_>, Error> {
            Ok(id
                .attach(&self.repo)
                .object()?
                .peel_to_kind(git::object::Kind::Tree)?
                .into_tree())
        };
        let from = into_tree(from.into())?;
        let to = into_tree(to.into())?;
        let mut delegate = Delegate::default();
        from.changes()
            .track_filename()
            .for_each_to_obtain_tree(&to, |change| delegate.handle(change))?;
        delegate.into_result()
    }
}

/// Find changes while changing the underlying repository in one way or another.
impl Index {
    /// As `fetch_changes_with_options`, but without the options.
    pub fn fetch_changes(&self) -> Result<Vec<Change>, Error> {
        self.fetch_changes_with_options(None)
    }

    /// Return all `Change`s that are observed between the last time this method was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will be created or adjusted to point to the latest fetched
    /// state, which causes this method to have a different result each time it is called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    pub fn fetch_changes_with_options(
        &self,
        options: Option<&mut git2::FetchOptions<'_>>,
    ) -> Result<Vec<Change>, Error> {
        let (changes, to) = self.peek_changes_with_options(options)?;
        self.set_last_seen_reference(to)?;
        Ok(changes)
    }

    /// Set the last seen reference to the given Oid. It will be created if it does not yet exists.
    pub fn set_last_seen_reference(&self, to: git::hash::ObjectId) -> Result<(), Error> {
        let repo = self.repository();
        repo.reference(
            self.seen_ref_name,
            to,
            PreviousValue::Any,
            "updating seen-ref head to latest fetched commit",
        )?;
        Ok(())
    }

    /// Return all `CreateVersion`s observed between `from` and `to`. Both parameter are ref-specs
    /// pointing to either a commit or a tree.
    /// Learn more about specifying revisions
    /// in the
    /// [official documentation](https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html)
    pub fn changes(
        &self,
        from: impl AsRef<str>,
        to: impl AsRef<str>,
    ) -> Result<Vec<Change>, Error> {
        let repo = self.repository();
        let from = repo
            .rev_parse(from.as_ref())?
            .single()
            .expect("revspec was not a range")
            .detach();
        let to = repo
            .rev_parse(to.as_ref())?
            .single()
            .expect("revspec was not a range")
            .detach();
        self.changes_between_commits(from, to)
    }
}
<commit_msg>Add `fetch_changes_with_options()` using gitoxide<commit_after>use crate::{Change, Index};
use git_repository as git;
use git_repository::prelude::ObjectIdExt;
use git_repository::refs::transaction::PreviousValue;
use std::convert::TryFrom;
use std::sync::atomic::AtomicBool;

mod delegate;
use delegate::Delegate;

/// The error returned by methods dealing with obtaining index changes.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
    #[error("Failed to fetch crates.io index repository")]
    FetchGit2(#[from] git2::Error),
    #[error("Couldn't update marker reference")]
    ReferenceEdit(#[from] git::reference::edit::Error),
    #[error("Failed to parse rev-spec to determine which revisions to diff")]
    RevParse(#[from] git::revision::spec::parse::Error),
    #[error("Couldn't find blob that showed up when diffing trees")]
    FindObject(#[from] git::object::find::existing::Error),
    #[error("Couldn't get the tree of a commit for diffing purposes")]
    PeelToTree(#[from] git::object::peel::to_kind::Error),
    #[error("Failed to diff two trees to find changed crates")]
    Diff(#[from] git::object::tree::diff::Error),
    #[error("Failed to decode {line:?} in file {file_name:?} as crate version")]
    VersionDecode {
        source: serde_json::Error,
        file_name: bstr::BString,
        line: bstr::BString,
    },
    #[error(transparent)]
    FindRemote(#[from] git::remote::find::existing::Error),
    #[error(transparent)]
    FindReference(#[from] git::reference::find::existing::Error),
    #[error(transparent)]
    Connect(#[from] git::remote::connect::Error),
    #[error(transparent)]
    PrepareFetch(#[from] git::remote::fetch::prepare::Error),
    #[error(transparent)]
    Fetch(#[from] git::remote::fetch::Error),
    #[error(transparent)]
    InitAnonymousRemote(#[from] git::remote::init::Error),
    #[error("Could not find local tracking branch for remote branch {name:?} in any of {} fetched refs", mappings.len())]
    NoMatchingBranch {
        name: String,
        mappings: Vec<git::remote::fetch::Mapping>,
    },
}

/// Find changes without modifying the underling repository
impl Index {
    /// As `peek_changes_with_options`, but without the options.
    pub fn peek_changes(&self) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        self.peek_changes_with_options(None)
    }

    /// Return all `Change`s that are observed between the last time `fetch_changes(…)` was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will not be created or updated.
    /// The second field in the returned tuple is the commit object to which the changes were provided.
    /// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
    /// as if `fetch_changes(…)` had been called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    pub fn peek_changes_with_options(
        &self,
        options: Option<&mut git2::FetchOptions<'_>>,
    ) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        let repo = &self.repo;
        let from = repo
            .find_reference(self.seen_ref_name)
            .ok()
            .and_then(|r| r.try_id().map(|id| id.detach()))
            .unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
        let remote_name = self
            .remote_name
            .expect("always set for this old portion of the code");
        let to = {
            let repo = git2::Repository::open(repo.git_dir())?;
            repo.find_remote(remote_name).and_then(|mut r| {
                r.fetch(
                    &[format!(
                        "+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
                        remote = remote_name,
                        branch = self.branch_name,
                    )],
                    options,
                    None,
                )
            })?;
            git::hash::ObjectId::try_from(
                repo.refname_to_id(&format!(
                    "refs/remotes/{}/{}",
                    remote_name, self.branch_name
                ))?
                .as_bytes(),
            )
            .expect("valid oid")
        };

        Ok((self.changes_between_commits(from, to)?, to))
    }

    /// Return all `Change`s that are observed between the last time `peek_changes*(…)` was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin` or whatever is configured for the current `HEAD` branch and lastly
    /// what it should be based on knowledge about he crates index.
    /// The `last_seen_reference()` will not be created or updated.
    /// The second field in the returned tuple is the commit object to which the changes were provided.
    /// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
    /// as if `fetch_changes(…)` had been called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    // TODO: update this once it's clear how auto-gc works in `gitoxide`.
    pub fn peek_changes_with_options2(
        &self,
        progress: impl git::Progress,
        should_interrupt: &AtomicBool,
    ) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
        let repo = &self.repo;
        let from = repo
            .find_reference(self.seen_ref_name)
            .ok()
            .and_then(|r| r.try_id().map(|id| id.detach()))
            .unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
        let to = {
            let mut remote = self
                .remote_name
                .and_then(|name| {
                    self.repo.find_remote(name).ok().or_else(|| {
                        self.repo
                            .head()
                            .ok()
                            .and_then(|head| {
                                head.into_remote(git::remote::Direction::Fetch)
                                    .map(|r| r.ok())
                                    .flatten()
                            })
                            .or_else(|| {
                                self.repo
                                    .find_default_remote(git::remote::Direction::Fetch)
                                    .map(|r| r.ok())
                                    .flatten()
                            })
                    })
                })
                .map(Ok)
                .unwrap_or_else(|| {
                    self.repo
                        .head()?
                        .into_remote(git::remote::Direction::Fetch)
                        .map(|r| r.map_err(Error::from))
                        .or_else(|| {
                            self.repo
                                .find_default_remote(git::remote::Direction::Fetch)
                                .map(|r| r.map_err(Error::from))
                        })
                        .unwrap_or_else(|| {
                            self.repo
                                .remote_at("https://github.com/rust-lang/crates.io-index")
                                .map_err(Into::into)
                        })
                })?;
            if remote.refspecs(git::remote::Direction::Fetch).is_empty() {
                let spec = format!(
                    "+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
                    remote = self.remote_name.unwrap_or("origin"),
                    branch = self.branch_name,
                );
                remote
                    .replace_refspecs(Some(spec.as_str()), git::remote::Direction::Fetch)
                    .expect("valid statically known refspec");
            }
            let res: git::remote::fetch::Outcome<'_> = remote
                .connect(git::remote::Direction::Fetch, progress)?
                .prepare_fetch(Default::default())?
                .receive(should_interrupt)?;
            let branch_name = format!("refs/heads/{}", self.branch_name);
            let local_tracking = res
                .ref_map
                .mappings
                .iter()
                .find_map(|m| match &m.remote {
                    git::remote::fetch::Source::Ref(r) => (r.unpack().0 == branch_name)
                        .then(|| m.local.as_ref())
                        .flatten(),
                    _ => None,
                })
                .ok_or_else(|| Error::NoMatchingBranch {
                    name: branch_name,
                    mappings: res.ref_map.mappings.clone(),
                })?;
            self.repo
                .find_reference(local_tracking)
                .expect("local tracking branch exists if we see it here")
                .id()
                .detach()
        };

        Ok((self.changes_between_commits(from, to)?, to))
    }

    /// Similar to `changes()`, but requires `from` and `to` objects to be provided. They may point
    /// to either `Commit`s or `Tree`s.
    pub fn changes_between_commits(
        &self,
        from: impl Into<git::hash::ObjectId>,
        to: impl Into<git::hash::ObjectId>,
    ) -> Result<Vec<Change>, Error> {
        let into_tree = |id: git::hash::ObjectId| -> Result<git::Tree<'_>, Error> {
            Ok(id
                .attach(&self.repo)
                .object()?
                .peel_to_kind(git::object::Kind::Tree)?
                .into_tree())
        };
        let from = into_tree(from.into())?;
        let to = into_tree(to.into())?;
        let mut delegate = Delegate::default();
        from.changes()
            .track_filename()
            .for_each_to_obtain_tree(&to, |change| delegate.handle(change))?;
        delegate.into_result()
    }
}

/// Find changes while changing the underlying repository in one way or another.
impl Index {
    /// As `fetch_changes_with_options`, but without the options.
    pub fn fetch_changes(&self) -> Result<Vec<Change>, Error> {
        self.fetch_changes_with_options(None)
    }

    /// Return all `Change`s that are observed between the last time this method was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will be created or adjusted to point to the latest fetched
    /// state, which causes this method to have a different result each time it is called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    pub fn fetch_changes_with_options(
        &self,
        options: Option<&mut git2::FetchOptions<'_>>,
    ) -> Result<Vec<Change>, Error> {
        let (changes, to) = self.peek_changes_with_options(options)?;
        self.set_last_seen_reference(to)?;
        Ok(changes)
    }

    /// Return all `Change`s that are observed between the last time this method was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will be created or adjusted to point to the latest fetched
    /// state, which causes this method to have a different result each time it is called.
    ///
    /// # Resource Usage
    ///
    /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
    /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
    /// of the maximum number of open file handles as configured with `ulimit`.
    ///
    /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
    pub fn fetch_changes_with_options2(
        &self,
        progress: impl git::Progress,
        should_interrupt: &AtomicBool,
    ) -> Result<Vec<Change>, Error> {
        let (changes, to) = self.peek_changes_with_options2(progress, should_interrupt)?;
        self.set_last_seen_reference(to)?;
        Ok(changes)
    }

    /// Set the last seen reference to the given Oid. It will be created if it does not yet exists.
    pub fn set_last_seen_reference(&self, to: git::hash::ObjectId) -> Result<(), Error> {
        let repo = self.repository();
        repo.reference(
            self.seen_ref_name,
            to,
            PreviousValue::Any,
            "updating seen-ref head to latest fetched commit",
        )?;
        Ok(())
    }

    /// Return all `CreateVersion`s observed between `from` and `to`. Both parameter are ref-specs
    /// pointing to either a commit or a tree.
    /// Learn more about specifying revisions
    /// in the
    /// [official documentation](https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html)
    pub fn changes(
        &self,
        from: impl AsRef<str>,
        to: impl AsRef<str>,
    ) -> Result<Vec<Change>, Error> {
        let repo = self.repository();
        let from = repo
            .rev_parse(from.as_ref())?
            .single()
            .expect("revspec was not a range")
            .detach();
        let to = repo
            .rev_parse(to.as_ref())?
            .single()
            .expect("revspec was not a range")
            .detach();
        self.changes_between_commits(from, to)
    }
}
<|endoftext|>


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

// Some basic logging
macro_rules! rtdebug_ (
    ($( $arg:expr),+) => ( {
        dumb_println(fmt!( $($arg),+ ));

        fn dumb_println(s: &str) {
            use str::as_c_str;
            use libc::c_char;

            extern {
                fn printf(s: *c_char);
            }

            do as_c_str(s.to_str() + "\n") |s| {
                unsafe { printf(s); }
            }
        }

    } )
)

// An alternate version with no output, for turning off logging
macro_rules! rtdebug (
    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
)

mod sched;
mod io;
pub mod uvll;
mod uvio;
mod uv;
// FIXME #5248: The import in `sched` doesn't resolve unless this is pub!
pub mod thread_local_storage;
mod work_queue;
mod stack;
mod context;
mod thread;
pub mod env;
<commit_msg>core: Don't use printf in rtdebug!<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Some basic logging
macro_rules! rtdebug_ (
    ($( $arg:expr),+) => ( {
        dumb_println(fmt!( $($arg),+ ));

        fn dumb_println(s: &str) {
            use io::WriterUtil;
            let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
            dbg.write_str(s);
            dbg.write_str("\n");
        }

    } )
)

// An alternate version with no output, for turning off logging
macro_rules! rtdebug (
    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
)

mod sched;
mod io;
pub mod uvll;
mod uvio;
mod uv;
// FIXME #5248: The import in `sched` doesn't resolve unless this is pub!
pub mod thread_local_storage;
mod work_queue;
mod stack;
mod context;
mod thread;
pub mod env;
<|endoftext|>


<commit_before>use std::collections::HashMap;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}
<commit_msg>Add `StreamInfo::is_varied_block_size`<commit_after>use std::collections::HashMap;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}
<|endoftext|>


<commit_before>//! Iron's Middleware and Handler System
//!
//! Iron's Middleware system is best modeled with a diagram.
//!
//! ```plain
//! [b] = BeforeMiddleware
//! [a] = AfterMiddleware
//! [[h]] = AroundMiddleware
//! [h] = Handler
//! ```
//!
//! With no errors, the flow looks like:
//!
//! ```plain
//! [b] -> [b] -> [b] -> [[[[h]]]] -> [a] -> [a] -> [a] -> [a]
//! ```
//!
//! A request first travels through all BeforeMiddleware, then a Response is generated
//! by the Handler, which can be an arbitrary nesting of AroundMiddleware, then all
//! AfterMiddleware are called with both the Request and Response. After all AfterMiddleware
//! have been fired, the response is written back to the client.
//!
//! Iron's error handling system is pragmatic and focuses on tracking two pieces
//! of information for error receivers (other middleware):
//!
//! * The cause of the error
//! * The result (what to do about) the error.
//!
//! The cause of the error is represented simply by the error itself, and the result
//! of the error, representing the action to take in response to the error, is a complete
//! Response, which will be sent at the end of the error flow.
//!
//! When an error is thrown in Iron by any middleware or handler returning an `Err`
//! variant with an `IronError`, the flow of the Request switches to the error flow,
//! which proceeds to just call the `catch` method of middleware and sidesteps the
//! `Handler` entirely, since there is already a Response in the error.
//!
//! A Request can exit the error flow by returning an Ok from any of the catch methods.
//! This resumes the flow at the middleware immediately following the middleware which
//! handled the error. It is impossible to "go back" to an earlier middleware that was
//! skipped.
//!
//! Generally speaking, returning a 5XX error code means that the error flow should be
//! entered by raising an explicit error. Dealing with 4XX errors is trickier, since
//! the server may not want to recognize an error that is entirely the clients fault;
//! handling of 4XX error codes is up to to each application and middleware author.
//!
//! Middleware authors should be cognizant that their middleware may be skipped during
//! the error flow. Anything that *must* be done to each Request or Response should
//! be run during both the normal and error flow by implementing the `catch` method to
//! also do the necessary action.
//!

use {Request, Response, IronResult, IronError};

/// `Handler`s are responsible for handling requests by creating Responses from Requests.
pub trait Handler: Send + Sync + 'static {
    /// Produce a `Response` from a Request, with the possibility of error.
    fn handle(&self, &mut Request) -> IronResult<Response>;
}

/// `BeforeMiddleware` are fired before a `Handler` is called inside of a Chain.
///
/// `BeforeMiddleware` are responsible for doing request pre-processing that requires
/// the ability to change control-flow, such as authorization middleware, or for editing
/// the request by modifying the headers.
///
/// `BeforeMiddleware` only have access to the Request, if you need to modify or read
/// a Response, you will need `AfterMiddleware`. Middleware which wishes to send an
/// early response that is not an error cannot be `BeforeMiddleware`, but should
/// instead be `AroundMiddleware`.
pub trait BeforeMiddleware: Send + Sync + 'static {
    /// Do whatever work this middleware should do with a `Request` object.
    fn before(&self, _: &mut Request) -> IronResult<()> { Ok(()) }

    /// Respond to an error thrown by a previous `BeforeMiddleware`.
    ///
    /// Returning a `Ok` will cause the request to resume the normal flow at the
    /// next `BeforeMiddleware`, or if this was the last `BeforeMiddleware`,
    /// at the `Handler`.
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<()> { Err(err) }
}

/// `AfterMiddleware` are fired after a `Handler` is called inside of a Chain.
///
/// `AfterMiddleware` receive both a `Request` and a `Response` and are responsible for doing
/// any response post-processing.
///
/// `AfterMiddleware` should *not* overwrite the contents of a Response. In
/// the common case, a complete response is generated by the Chain's `Handler` and
/// `AfterMiddleware` simply do post-processing of that Response, such as
/// adding headers or logging.
pub trait AfterMiddleware: Send + Sync + 'static {
    /// Do whatever post-processing this middleware should do.
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(res)
    }

    /// Respond to an error thrown by previous `AfterMiddleware`, the `Handler`,
    /// or a `BeforeMiddleware`.
    ///
    /// Returning `Ok` will cause the request to resume the normal flow at the
    /// next `AfterMiddleware`.
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        Err(err)
    }
}

/// AroundMiddleware are used to wrap and replace the `Handler` in a `Chain`.
///
/// AroundMiddleware produce `Handler`s through their `around` method, which is
/// called once on insertion into a Chain or can be called manually outside of a
/// `Chain`.
pub trait AroundMiddleware {
    /// Produce a `Handler` from this `AroundMiddleware` given another `Handler`.
    ///
    /// Usually this means wrapping the handler and editing the `Request` on the
    /// way in and the `Response` on the way out.
    ///
    /// This is called only once, when an `AroundMiddleware` is added to a `Chain`
    /// using `Chain::around`, it is passed the `Chain`'s current `Handler`.
    fn around(mut self, handler: Box<Handler>) -> Box<Handler>;
}

/// The middleware chain used in Iron.
///
/// This is a canonical implementation of Iron's middleware system,
/// but Iron's infrastructure is flexible enough to allow alternate
/// systems.
pub struct Chain{
    befores: Vec<Box<BeforeMiddleware>>,
    afters: Vec<Box<AfterMiddleware>>,

    // Internal invariant: this is always Some
    handler: Option<Box<Handler>>
}

impl Chain {
    /// Construct a new ChainBuilder from a `Handler`.
    pub fn new<H: Handler>(handler: H) -> Chain {
        Chain {
            befores: vec![],
            afters: vec![],
            handler: Some(Box::new(handler) as Box<Handler>)
        }
    }

    /// Link both a before and after middleware to the chain at once.
    ///
    /// Middleware that have a Before and After piece should have a constructor
    /// which returns both as a tuple, so it can be passed directly to link.
    pub fn link<B, A>(&mut self, link: (B, A))
    where A: AfterMiddleware, B: BeforeMiddleware {
        let (before, after) = link;
        self.befores.push(Box::new(before) as Box<BeforeMiddleware>);
        self.afters.push(Box::new(after) as Box<AfterMiddleware>);
    }

    /// Link a `BeforeMiddleware` to the `Chain`, after all previously linked
    /// `BeforeMiddleware`.
    pub fn link_before<B>(&mut self, before: B) where B: BeforeMiddleware {
        self.befores.push(Box::new(before) as Box<BeforeMiddleware>);
    }

    /// Link a `AfterMiddleware` to the `Chain`, after all previously linked
    /// `AfterMiddleware`.
    pub fn link_after<A>(&mut self, after: A) where A: AfterMiddleware {
        self.afters.push(Box::new(after) as Box<AfterMiddleware>);
    }

    /// Apply an `AroundMiddleware` to the `Handler` in this `Chain`.
    pub fn around<A>(&mut self, around: A) where A: AroundMiddleware {
        let mut handler = self.handler.take().unwrap();
        handler = around.around(handler);
        self.handler = Some(handler);
    }
}

impl Handler for Chain {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // Kick off at befores, which will continue into handler
        // then afters.
        self.continue_from_before(req, 0)
    }
}

impl Chain {
    ///////////////// Implementation Helpers /////////////////

    // Enter the error flow from a before middleware, starting
    // at the passed index.
    //
    // If the index is out of bounds for the before middleware Vec,
    // this instead behaves the same as fail_from_handler.
    fn fail_from_before(&self, req: &mut Request, index: usize,
                        mut err: IronError) -> IronResult<Response> {
        // If this was the last before, yield to next phase.
        if index >= self.befores.len() {
            return self.fail_from_handler(req, err)
        }

        for (i, before) in self.befores[index..].iter().enumerate() {
            err = match before.catch(req, err) {
                Err(err) => err,
                Ok(()) => return self.continue_from_before(req, index + i + 1)
            };
        }

        // Next phase
        self.fail_from_handler(req, err)
    }

    // Enter the error flow from an errored handle, starting with the
    // first AfterMiddleware.
    fn fail_from_handler(&self, req: &mut Request,
                         err: IronError) -> IronResult<Response> {
        // Yield to next phase, nothing to do here.
        self.fail_from_after(req, 0, err)
    }

    // Enter the error flow from an errored after middleware, starting
    // with the passed index.
    //
    // If the index is out of bounds for the after middleware Vec,
    // this instead just returns the passed error.
    fn fail_from_after(&self, req: &mut Request, index: usize,
                       mut err: IronError) -> IronResult<Response> {
        // If this was the last after, we're done.
        if index == self.afters.len() { return Err(err) }

        for (i, after) in self.afters[index..].iter().enumerate() {
            err = match after.catch(req, err) {
                Err(err) => err,
                Ok(res) => return self.continue_from_after(req, index + i + 1, res)
            }
        }

        // Done
        Err(err)
    }

    // Enter the normal flow in the before middleware, starting with the passed
    // index.
    fn continue_from_before(&self, req: &mut Request,
                            index: usize) -> IronResult<Response> {
        // If this was the last beforemiddleware, start at the handler.
        if index >= self.befores.len() {
            return self.continue_from_handler(req)
        }

        for (i, before) in self.befores[index..].iter().enumerate() {
            match before.before(req) {
                Ok(()) => {},
                Err(err) => return self.fail_from_before(req, index + i + 1, err)
            }
        }

        // Yield to next phase.
        self.continue_from_handler(req)
    }

    // Enter the normal flow at the handler.
    fn continue_from_handler(&self, req: &mut Request) -> IronResult<Response> {
        // unwrap is safe because it's always Some
        match self.handler.as_ref().unwrap().handle(req) {
            Ok(res) => self.continue_from_after(req, 0, res),
            Err(err) => self.fail_from_handler(req, err)
        }
    }

    // Enter the normal flow in the after middleware, starting with the passed
    // index.
    fn continue_from_after(&self, req: &mut Request, index: usize,
                            mut res: Response) -> IronResult<Response> {
        // If this was the last after middleware, we're done.
        if index >= self.afters.len() {
            return Ok(res);
        }

        for (i, after) in self.afters[index..].iter().enumerate() {
            res = match after.after(req, res) {
                Ok(r) => r,
                Err(err) => return self.fail_from_after(req, index + i + 1, err)
            }
        }

        // We made it with no error!
        Ok(res)
    }
}

impl<F> Handler for F
where F: Send + Sync + 'static + Fn(&mut Request) -> IronResult<Response> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        (*self)(req)
    }
}

impl Handler for Box<Handler> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        (**self).handle(req)
    }
}

impl<F> BeforeMiddleware for F
where F: Send + Sync + 'static + Fn(&mut Request) -> IronResult<()> {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        (*self)(req)
    }
}

impl BeforeMiddleware for Box<BeforeMiddleware> {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        (**self).before(req)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<()> {
        (**self).catch(req, err)
    }
}

impl<F> AfterMiddleware for F
where F: Send + Sync + 'static + Fn(&mut Request, Response) -> IronResult<Response> {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        (*self)(req, res)
    }
}

impl AfterMiddleware for Box<AfterMiddleware> {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        (**self).after(req, res)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
        (**self).catch(req, err)
    }
}

#[cfg(test)]
mod test;

<commit_msg>(feat) Add Any bounds on Handler, BeforeMiddleware, and AfterMiddleware.<commit_after>//! Iron's Middleware and Handler System
//!
//! Iron's Middleware system is best modeled with a diagram.
//!
//! ```plain
//! [b] = BeforeMiddleware
//! [a] = AfterMiddleware
//! [[h]] = AroundMiddleware
//! [h] = Handler
//! ```
//!
//! With no errors, the flow looks like:
//!
//! ```plain
//! [b] -> [b] -> [b] -> [[[[h]]]] -> [a] -> [a] -> [a] -> [a]
//! ```
//!
//! A request first travels through all BeforeMiddleware, then a Response is generated
//! by the Handler, which can be an arbitrary nesting of AroundMiddleware, then all
//! AfterMiddleware are called with both the Request and Response. After all AfterMiddleware
//! have been fired, the response is written back to the client.
//!
//! Iron's error handling system is pragmatic and focuses on tracking two pieces
//! of information for error receivers (other middleware):
//!
//! * The cause of the error
//! * The result (what to do about) the error.
//!
//! The cause of the error is represented simply by the error itself, and the result
//! of the error, representing the action to take in response to the error, is a complete
//! Response, which will be sent at the end of the error flow.
//!
//! When an error is thrown in Iron by any middleware or handler returning an `Err`
//! variant with an `IronError`, the flow of the Request switches to the error flow,
//! which proceeds to just call the `catch` method of middleware and sidesteps the
//! `Handler` entirely, since there is already a Response in the error.
//!
//! A Request can exit the error flow by returning an Ok from any of the catch methods.
//! This resumes the flow at the middleware immediately following the middleware which
//! handled the error. It is impossible to "go back" to an earlier middleware that was
//! skipped.
//!
//! Generally speaking, returning a 5XX error code means that the error flow should be
//! entered by raising an explicit error. Dealing with 4XX errors is trickier, since
//! the server may not want to recognize an error that is entirely the clients fault;
//! handling of 4XX error codes is up to to each application and middleware author.
//!
//! Middleware authors should be cognizant that their middleware may be skipped during
//! the error flow. Anything that *must* be done to each Request or Response should
//! be run during both the normal and error flow by implementing the `catch` method to
//! also do the necessary action.
//!

use std::any::Any;
use {Request, Response, IronResult, IronError};

/// `Handler`s are responsible for handling requests by creating Responses from Requests.
pub trait Handler: Send + Sync + Any {
    /// Produce a `Response` from a Request, with the possibility of error.
    fn handle(&self, &mut Request) -> IronResult<Response>;
}

/// `BeforeMiddleware` are fired before a `Handler` is called inside of a Chain.
///
/// `BeforeMiddleware` are responsible for doing request pre-processing that requires
/// the ability to change control-flow, such as authorization middleware, or for editing
/// the request by modifying the headers.
///
/// `BeforeMiddleware` only have access to the Request, if you need to modify or read
/// a Response, you will need `AfterMiddleware`. Middleware which wishes to send an
/// early response that is not an error cannot be `BeforeMiddleware`, but should
/// instead be `AroundMiddleware`.
pub trait BeforeMiddleware: Send + Sync + Any {
    /// Do whatever work this middleware should do with a `Request` object.
    fn before(&self, _: &mut Request) -> IronResult<()> { Ok(()) }

    /// Respond to an error thrown by a previous `BeforeMiddleware`.
    ///
    /// Returning a `Ok` will cause the request to resume the normal flow at the
    /// next `BeforeMiddleware`, or if this was the last `BeforeMiddleware`,
    /// at the `Handler`.
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<()> { Err(err) }
}

/// `AfterMiddleware` are fired after a `Handler` is called inside of a Chain.
///
/// `AfterMiddleware` receive both a `Request` and a `Response` and are responsible for doing
/// any response post-processing.
///
/// `AfterMiddleware` should *not* overwrite the contents of a Response. In
/// the common case, a complete response is generated by the Chain's `Handler` and
/// `AfterMiddleware` simply do post-processing of that Response, such as
/// adding headers or logging.
pub trait AfterMiddleware: Send + Sync + Any {
    /// Do whatever post-processing this middleware should do.
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(res)
    }

    /// Respond to an error thrown by previous `AfterMiddleware`, the `Handler`,
    /// or a `BeforeMiddleware`.
    ///
    /// Returning `Ok` will cause the request to resume the normal flow at the
    /// next `AfterMiddleware`.
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        Err(err)
    }
}

/// AroundMiddleware are used to wrap and replace the `Handler` in a `Chain`.
///
/// AroundMiddleware produce `Handler`s through their `around` method, which is
/// called once on insertion into a Chain or can be called manually outside of a
/// `Chain`.
pub trait AroundMiddleware {
    /// Produce a `Handler` from this `AroundMiddleware` given another `Handler`.
    ///
    /// Usually this means wrapping the handler and editing the `Request` on the
    /// way in and the `Response` on the way out.
    ///
    /// This is called only once, when an `AroundMiddleware` is added to a `Chain`
    /// using `Chain::around`, it is passed the `Chain`'s current `Handler`.
    fn around(self, handler: Box<Handler>) -> Box<Handler>;
}

/// The middleware chain used in Iron.
///
/// This is a canonical implementation of Iron's middleware system,
/// but Iron's infrastructure is flexible enough to allow alternate
/// systems.
pub struct Chain {
    befores: Vec<Box<BeforeMiddleware>>,
    afters: Vec<Box<AfterMiddleware>>,

    // Internal invariant: this is always Some
    handler: Option<Box<Handler>>
}

impl Chain {
    /// Construct a new ChainBuilder from a `Handler`.
    pub fn new<H: Handler>(handler: H) -> Chain {
        Chain {
            befores: vec![],
            afters: vec![],
            handler: Some(Box::new(handler) as Box<Handler>)
        }
    }

    /// Link both a before and after middleware to the chain at once.
    ///
    /// Middleware that have a Before and After piece should have a constructor
    /// which returns both as a tuple, so it can be passed directly to link.
    pub fn link<B, A>(&mut self, link: (B, A))
    where A: AfterMiddleware, B: BeforeMiddleware {
        let (before, after) = link;
        self.befores.push(Box::new(before) as Box<BeforeMiddleware>);
        self.afters.push(Box::new(after) as Box<AfterMiddleware>);
    }

    /// Link a `BeforeMiddleware` to the `Chain`, after all previously linked
    /// `BeforeMiddleware`.
    pub fn link_before<B>(&mut self, before: B) where B: BeforeMiddleware {
        self.befores.push(Box::new(before) as Box<BeforeMiddleware>);
    }

    /// Link a `AfterMiddleware` to the `Chain`, after all previously linked
    /// `AfterMiddleware`.
    pub fn link_after<A>(&mut self, after: A) where A: AfterMiddleware {
        self.afters.push(Box::new(after) as Box<AfterMiddleware>);
    }

    /// Apply an `AroundMiddleware` to the `Handler` in this `Chain`.
    pub fn around<A>(&mut self, around: A) where A: AroundMiddleware {
        let mut handler = self.handler.take().unwrap();
        handler = around.around(handler);
        self.handler = Some(handler);
    }
}

impl Handler for Chain {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // Kick off at befores, which will continue into handler
        // then afters.
        self.continue_from_before(req, 0)
    }
}

impl Chain {
    ///////////////// Implementation Helpers /////////////////

    // Enter the error flow from a before middleware, starting
    // at the passed index.
    //
    // If the index is out of bounds for the before middleware Vec,
    // this instead behaves the same as fail_from_handler.
    fn fail_from_before(&self, req: &mut Request, index: usize,
                        mut err: IronError) -> IronResult<Response> {
        // If this was the last before, yield to next phase.
        if index >= self.befores.len() {
            return self.fail_from_handler(req, err)
        }

        for (i, before) in self.befores[index..].iter().enumerate() {
            err = match before.catch(req, err) {
                Err(err) => err,
                Ok(()) => return self.continue_from_before(req, index + i + 1)
            };
        }

        // Next phase
        self.fail_from_handler(req, err)
    }

    // Enter the error flow from an errored handle, starting with the
    // first AfterMiddleware.
    fn fail_from_handler(&self, req: &mut Request,
                         err: IronError) -> IronResult<Response> {
        // Yield to next phase, nothing to do here.
        self.fail_from_after(req, 0, err)
    }

    // Enter the error flow from an errored after middleware, starting
    // with the passed index.
    //
    // If the index is out of bounds for the after middleware Vec,
    // this instead just returns the passed error.
    fn fail_from_after(&self, req: &mut Request, index: usize,
                       mut err: IronError) -> IronResult<Response> {
        // If this was the last after, we're done.
        if index == self.afters.len() { return Err(err) }

        for (i, after) in self.afters[index..].iter().enumerate() {
            err = match after.catch(req, err) {
                Err(err) => err,
                Ok(res) => return self.continue_from_after(req, index + i + 1, res)
            }
        }

        // Done
        Err(err)
    }

    // Enter the normal flow in the before middleware, starting with the passed
    // index.
    fn continue_from_before(&self, req: &mut Request,
                            index: usize) -> IronResult<Response> {
        // If this was the last beforemiddleware, start at the handler.
        if index >= self.befores.len() {
            return self.continue_from_handler(req)
        }

        for (i, before) in self.befores[index..].iter().enumerate() {
            match before.before(req) {
                Ok(()) => {},
                Err(err) => return self.fail_from_before(req, index + i + 1, err)
            }
        }

        // Yield to next phase.
        self.continue_from_handler(req)
    }

    // Enter the normal flow at the handler.
    fn continue_from_handler(&self, req: &mut Request) -> IronResult<Response> {
        // unwrap is safe because it's always Some
        match self.handler.as_ref().unwrap().handle(req) {
            Ok(res) => self.continue_from_after(req, 0, res),
            Err(err) => self.fail_from_handler(req, err)
        }
    }

    // Enter the normal flow in the after middleware, starting with the passed
    // index.
    fn continue_from_after(&self, req: &mut Request, index: usize,
                            mut res: Response) -> IronResult<Response> {
        // If this was the last after middleware, we're done.
        if index >= self.afters.len() {
            return Ok(res);
        }

        for (i, after) in self.afters[index..].iter().enumerate() {
            res = match after.after(req, res) {
                Ok(r) => r,
                Err(err) => return self.fail_from_after(req, index + i + 1, err)
            }
        }

        // We made it with no error!
        Ok(res)
    }
}

impl<F> Handler for F
where F: Send + Sync + Any + Fn(&mut Request) -> IronResult<Response> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        (*self)(req)
    }
}

impl Handler for Box<Handler> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        (**self).handle(req)
    }
}

impl<F> BeforeMiddleware for F
where F: Send + Sync + Any + Fn(&mut Request) -> IronResult<()> {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        (*self)(req)
    }
}

impl BeforeMiddleware for Box<BeforeMiddleware> {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        (**self).before(req)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<()> {
        (**self).catch(req, err)
    }
}

impl<F> AfterMiddleware for F
where F: Send + Sync + Any + Fn(&mut Request, Response) -> IronResult<Response> {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        (*self)(req, res)
    }
}

impl AfterMiddleware for Box<AfterMiddleware> {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        (**self).after(req, res)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
        (**self).catch(req, err)
    }
}

impl<F> AroundMiddleware for F
where F: FnOnce(Box<Handler>) -> Box<Handler> {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        self(handler)
    }
}

#[cfg(test)]
mod test;

<|endoftext|>


<commit_before>use std::collections::HashMap;

use chrono::NaiveDateTime;
use diesel::prelude::*;

use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Dependency, User};
use crate::schema::*;

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug, Queryable, Deserialize, Serialize)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: String,
    pub updated_at: NaiveDateTime,
    pub created_at: NaiveDateTime,
    pub downloads: i32,
    pub features: serde_json::Value,
    pub yanked: bool,
    pub license: Option<String>,
    pub crate_size: Option<i32>,
    pub published_by: Option<i32>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: serde_json::Value,
    license: Option<String>,
    crate_size: Option<i32>,
    published_by: i32,
}

/// The highest version (semver order) and the most recently updated version.
/// Typically used for a single crate.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TopVersions {
    /// The "highest" version in terms of semver
    pub highest: Option<semver::Version>,
    /// The "highest" non-prerelease version
    pub highest_stable: Option<semver::Version>,
    /// The "newest" version in terms of publishing date
    pub newest: Option<semver::Version>,
}

impl TopVersions {
    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a list of `Version` instances.
    pub fn from_versions(versions: Vec<Version>) -> Self {
        Self::from_date_version_pairs(versions.into_iter().map(|v| (v.created_at, v.num)))
    }

    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a collection of date/version pairs.
    pub fn from_date_version_pairs<T>(pairs: T) -> Self
    where
        T: Clone + IntoIterator<Item = (NaiveDateTime, String)>,
    {
        // filter out versions that we can't parse
        let pairs: Vec<(NaiveDateTime, semver::Version)> = pairs
            .into_iter()
            .filter_map(|(date, version)| {
                semver::Version::parse(&version)
                    .ok()
                    .map(|version| (date, version))
            })
            .collect();

        let newest = pairs.iter().max().map(|(_, v)| v.clone());
        let highest = pairs.iter().map(|(_, v)| v).max().cloned();
        let highest_stable = pairs
            .iter()
            .map(|(_, v)| v)
            .filter(|v| v.pre.is_empty())
            .max()
            .cloned();

        Self {
            highest,
            highest_stable,
            newest,
        }
    }
}

impl Version {
    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn record_readme_rendering(version_id_: i32, conn: &PgConnection) -> QueryResult<usize> {
        use crate::schema::readme_renderings::dsl::*;
        use diesel::dsl::now;

        diesel::insert_into(readme_renderings)
            .values(version_id.eq(version_id_))
            .on_conflict(version_id)
            .do_update()
            .set(rendered_at.eq(now))
            .execute(conn)
    }

    /// Gets the User who ran `cargo publish` for this version, if recorded.
    /// Not for use when you have a group of versions you need the publishers for.
    pub fn published_by(&self, conn: &PgConnection) -> Option<User> {
        match self.published_by {
            Some(pb) => users::table.find(pb).first(conn).ok(),
            None => None,
        }
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
        crate_size: i32,
        published_by: i32,
    ) -> AppResult<Self> {
        let features = serde_json::to_value(features)?;

        let mut new_version = NewVersion {
            crate_id,
            num: num.to_string(),
            features,
            license,
            crate_size: Some(crate_size),
            published_by,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(&self, conn: &PgConnection, published_by_email: &str) -> AppResult<Version> {
        use crate::schema::versions::dsl::*;
        use diesel::dsl::exists;
        use diesel::{insert_into, select};

        conn.transaction(|| {
            let already_uploaded = versions
                .filter(crate_id.eq(self.crate_id))
                .filter(num.eq(&self.num));
            if select(exists(already_uploaded)).get_result(conn)? {
                return Err(cargo_err(&format_args!(
                    "crate version `{}` is already \
                     uploaded",
                    self.num
                )));
            }

            let version: Version = insert_into(versions).values(self).get_result(conn)?;

            insert_into(versions_published_by::table)
                .values((
                    versions_published_by::version_id.eq(version.id),
                    versions_published_by::email.eq(published_by_email),
                ))
                .execute(conn)?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> AppResult<()> {
        if let Some(ref license) = self.license {
            validate_license_expr(license)?;
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

fn validate_license_expr(s: &str) -> AppResult<()> {
    for part in s.split('/') {
        license_exprs::validate_license_expr(part).map_err(|e| {
            cargo_err(&format_args!("{}; see http://opensource.org/licenses for options, and http://spdx.org/licenses/ for their identifiers", e))
        })?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{validate_license_expr, TopVersions};
    use chrono::NaiveDateTime;

    #[track_caller]
    fn date(str: &str) -> NaiveDateTime {
        str.parse().unwrap()
    }

    #[track_caller]
    fn version(str: &str) -> semver::Version {
        semver::Version::parse(str).unwrap()
    }

    #[test]
    fn top_versions_empty() {
        let versions = vec![];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: None,
                highest_stable: None,
                newest: None,
            }
        );
    }

    #[test]
    fn top_versions_single() {
        let versions = vec![(date("2020-12-03T12:34:56"), "1.0.0".into())];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("1.0.0")),
                highest_stable: Some(version("1.0.0")),
                newest: Some(version("1.0.0")),
            }
        );
    }

    #[test]
    fn top_versions_prerelease() {
        let versions = vec![(date("2020-12-03T12:34:56"), "1.0.0-beta.5".into())];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("1.0.0-beta.5")),
                highest_stable: None,
                newest: Some(version("1.0.0-beta.5")),
            }
        );
    }

    #[test]
    fn top_versions_multiple() {
        let versions = vec![
            (date("2018-12-03T12:34:56"), "1.0.0".into()),
            (date("2019-12-03T12:34:56"), "2.0.0-alpha.1".into()),
            (date("2020-12-01T12:34:56"), "everything is broken".into()),
            (date("2020-12-03T12:34:56"), "1.1.0".into()),
            (date("2020-12-31T12:34:56"), "1.0.4".into()),
        ];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("2.0.0-alpha.1")),
                highest_stable: Some(version("1.1.0")),
                newest: Some(version("1.0.4")),
            }
        );
    }

    #[test]
    fn licenses() {
        assert_ok!(validate_license_expr("MIT"));
        assert_ok!(validate_license_expr("MIT OR Apache-2.0"));
        assert_ok!(validate_license_expr("MIT/Apache-2.0"));
        assert_ok!(validate_license_expr("MIT AND Apache-2.0"));

        let error = assert_err!(validate_license_expr("apache 2.0"));
        let error = format!("{}", error);
        assert!(error.starts_with("unknown license or other term: apache; see http"));

        let error = assert_err!(validate_license_expr("MIT OR (Apache-2.0 AND MIT)"));
        let error = format!("{}", error);
        assert!(error.starts_with("unknown license or other term: (Apache-2.0; see http"));
    }
}
<commit_msg>Auto merge of #4294 - Turbo87:clone-trait, r=Turbo87<commit_after>use std::collections::HashMap;

use chrono::NaiveDateTime;
use diesel::prelude::*;

use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Dependency, User};
use crate::schema::*;

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug, Queryable, Deserialize, Serialize)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: String,
    pub updated_at: NaiveDateTime,
    pub created_at: NaiveDateTime,
    pub downloads: i32,
    pub features: serde_json::Value,
    pub yanked: bool,
    pub license: Option<String>,
    pub crate_size: Option<i32>,
    pub published_by: Option<i32>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: serde_json::Value,
    license: Option<String>,
    crate_size: Option<i32>,
    published_by: i32,
}

/// The highest version (semver order) and the most recently updated version.
/// Typically used for a single crate.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TopVersions {
    /// The "highest" version in terms of semver
    pub highest: Option<semver::Version>,
    /// The "highest" non-prerelease version
    pub highest_stable: Option<semver::Version>,
    /// The "newest" version in terms of publishing date
    pub newest: Option<semver::Version>,
}

impl TopVersions {
    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a list of `Version` instances.
    pub fn from_versions(versions: Vec<Version>) -> Self {
        Self::from_date_version_pairs(versions.into_iter().map(|v| (v.created_at, v.num)))
    }

    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a collection of date/version pairs.
    pub fn from_date_version_pairs<T>(pairs: T) -> Self
    where
        T: IntoIterator<Item = (NaiveDateTime, String)>,
    {
        // filter out versions that we can't parse
        let pairs: Vec<(NaiveDateTime, semver::Version)> = pairs
            .into_iter()
            .filter_map(|(date, version)| {
                semver::Version::parse(&version)
                    .ok()
                    .map(|version| (date, version))
            })
            .collect();

        let newest = pairs.iter().max().map(|(_, v)| v.clone());
        let highest = pairs.iter().map(|(_, v)| v).max().cloned();
        let highest_stable = pairs
            .iter()
            .map(|(_, v)| v)
            .filter(|v| v.pre.is_empty())
            .max()
            .cloned();

        Self {
            highest,
            highest_stable,
            newest,
        }
    }
}

impl Version {
    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn record_readme_rendering(version_id_: i32, conn: &PgConnection) -> QueryResult<usize> {
        use crate::schema::readme_renderings::dsl::*;
        use diesel::dsl::now;

        diesel::insert_into(readme_renderings)
            .values(version_id.eq(version_id_))
            .on_conflict(version_id)
            .do_update()
            .set(rendered_at.eq(now))
            .execute(conn)
    }

    /// Gets the User who ran `cargo publish` for this version, if recorded.
    /// Not for use when you have a group of versions you need the publishers for.
    pub fn published_by(&self, conn: &PgConnection) -> Option<User> {
        match self.published_by {
            Some(pb) => users::table.find(pb).first(conn).ok(),
            None => None,
        }
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
        crate_size: i32,
        published_by: i32,
    ) -> AppResult<Self> {
        let features = serde_json::to_value(features)?;

        let mut new_version = NewVersion {
            crate_id,
            num: num.to_string(),
            features,
            license,
            crate_size: Some(crate_size),
            published_by,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(&self, conn: &PgConnection, published_by_email: &str) -> AppResult<Version> {
        use crate::schema::versions::dsl::*;
        use diesel::dsl::exists;
        use diesel::{insert_into, select};

        conn.transaction(|| {
            let already_uploaded = versions
                .filter(crate_id.eq(self.crate_id))
                .filter(num.eq(&self.num));
            if select(exists(already_uploaded)).get_result(conn)? {
                return Err(cargo_err(&format_args!(
                    "crate version `{}` is already \
                     uploaded",
                    self.num
                )));
            }

            let version: Version = insert_into(versions).values(self).get_result(conn)?;

            insert_into(versions_published_by::table)
                .values((
                    versions_published_by::version_id.eq(version.id),
                    versions_published_by::email.eq(published_by_email),
                ))
                .execute(conn)?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> AppResult<()> {
        if let Some(ref license) = self.license {
            validate_license_expr(license)?;
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

fn validate_license_expr(s: &str) -> AppResult<()> {
    for part in s.split('/') {
        license_exprs::validate_license_expr(part).map_err(|e| {
            cargo_err(&format_args!("{}; see http://opensource.org/licenses for options, and http://spdx.org/licenses/ for their identifiers", e))
        })?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::{validate_license_expr, TopVersions};
    use chrono::NaiveDateTime;

    #[track_caller]
    fn date(str: &str) -> NaiveDateTime {
        str.parse().unwrap()
    }

    #[track_caller]
    fn version(str: &str) -> semver::Version {
        semver::Version::parse(str).unwrap()
    }

    #[test]
    fn top_versions_empty() {
        let versions = vec![];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: None,
                highest_stable: None,
                newest: None,
            }
        );
    }

    #[test]
    fn top_versions_single() {
        let versions = vec![(date("2020-12-03T12:34:56"), "1.0.0".into())];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("1.0.0")),
                highest_stable: Some(version("1.0.0")),
                newest: Some(version("1.0.0")),
            }
        );
    }

    #[test]
    fn top_versions_prerelease() {
        let versions = vec![(date("2020-12-03T12:34:56"), "1.0.0-beta.5".into())];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("1.0.0-beta.5")),
                highest_stable: None,
                newest: Some(version("1.0.0-beta.5")),
            }
        );
    }

    #[test]
    fn top_versions_multiple() {
        let versions = vec![
            (date("2018-12-03T12:34:56"), "1.0.0".into()),
            (date("2019-12-03T12:34:56"), "2.0.0-alpha.1".into()),
            (date("2020-12-01T12:34:56"), "everything is broken".into()),
            (date("2020-12-03T12:34:56"), "1.1.0".into()),
            (date("2020-12-31T12:34:56"), "1.0.4".into()),
        ];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: Some(version("2.0.0-alpha.1")),
                highest_stable: Some(version("1.1.0")),
                newest: Some(version("1.0.4")),
            }
        );
    }

    #[test]
    fn licenses() {
        assert_ok!(validate_license_expr("MIT"));
        assert_ok!(validate_license_expr("MIT OR Apache-2.0"));
        assert_ok!(validate_license_expr("MIT/Apache-2.0"));
        assert_ok!(validate_license_expr("MIT AND Apache-2.0"));

        let error = assert_err!(validate_license_expr("apache 2.0"));
        let error = format!("{}", error);
        assert!(error.starts_with("unknown license or other term: apache; see http"));

        let error = assert_err!(validate_license_expr("MIT OR (Apache-2.0 AND MIT)"));
        let error = format!("{}", error);
        assert!(error.starts_with("unknown license or other term: (Apache-2.0; see http"));
    }
}
<|endoftext|>