<commit_before>use std::cell::{RefCell, Ref, Cell};
use std::io::SeekFrom;
use std::io::prelude::*;
use std::mem;
use std::path::Path;

use curl::easy::{Easy, List};
use git2;
use rustc_serialize::hex::ToHex;
use serde_json;
use url::Url;

use core::{PackageId, SourceId};
use ops;
use sources::git;
use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
use util::network;
use util::{FileLock, Filesystem, LazyCell};
use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl};
use util::errors::HttpError;

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    handle: LazyCell<RefCell<Easy>>,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str)
               -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config: config,
            tree: RefCell::new(None),
            handle: LazyCell::new(),
            repo: LazyCell::new(),
            head: Cell::new(None),
        }
    }

    fn easy(&self) -> CargoResult<&RefCell<Easy>> {
        self.handle.get_or_try_init(|| {
            ops::http_handle(self.config).map(RefCell::new)
        })
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.get_or_try_init(|| {
            let path = self.index_path.clone().into_path_unlocked();

            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    self.index_path.create_dir()?;
                    let lock = self.index_path.open_rw(Path::new(INDEX_LOCK),
                                                       self.config,
                                                       "the registry index")?;
                    let _ = lock.remove_siblings();
                    Ok(git2::Repository::init_bare(&path)?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let oid = self.repo()?.refname_to_id("refs/remotes/origin/master")?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<git2::Tree>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()))
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe {
            mem::transmute::<git2::Tree, git2::Tree<'static>>(tree)
        };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn load(&self, _root: &Path, path: &Path) -> CargoResult<Vec<u8>> {
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        let repo = self.repo()?;
        let tree = self.tree()?;
        let entry = tree.get_path(path)?;
        let object = entry.to_object(&repo)?;
        let blob = match object.as_blob() {
            Some(blob) => blob,
            None => bail!("path `{}` is not a blob in the git repo", path.display()),
        };
        Ok(blob.content().to_vec())
    }

    fn config(&mut self) -> CargoResult<Option<RegistryConfig>> {
        self.repo()?; // create intermediate dirs and initialize the repo
        let _lock = self.index_path.open_ro(Path::new(INDEX_LOCK),
                                            self.config,
                                            "the registry index")?;
        let json = self.load(Path::new(""), Path::new("config.json"))?;
        let config = serde_json::from_slice(&json)?;
        Ok(Some(config))
    }

    fn update_index(&mut self) -> CargoResult<()> {
        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        ops::http_handle(self.config)?;

        let repo = self.repo()?;
        let _lock = self.index_path.open_rw(Path::new(INDEX_LOCK),
                                            self.config,
                                            "the registry index")?;
        self.config.shell().status("Updating",
             format!("registry `{}`", self.source_id.url()))?;

        if self.source_id.url().host_str() == Some("github.com") {
            if let Ok(oid) = self.head() {
                let mut handle = self.easy()?.borrow_mut();
                debug!("attempting github fast path for {}",
                       self.source_id.url());
                if github_up_to_date(&mut handle, self.source_id.url(), &oid) {
                    return Ok(())
                }
                debug!("fast path failed, falling back to a git fetch");
            }
        }

        // git fetch origin master
        let url = self.source_id.url().to_string();
        let refspec = "refs/heads/master:refs/remotes/origin/master";
        git::fetch(&repo, &url, refspec, self.config).chain_error(|| {
            human(format!("failed to fetch `{}`", url))
        })?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str)
                -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        // Attempt to open an read-only copy first to avoid an exclusive write
        // lock and also work with read-only filesystems. Note that we check the
        // length of the file like below to handle interrupted downloads.
        //
        // If this fails then we fall through to the exclusive path where we may
        // have to redownload the file.
        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            let meta = dst.file().metadata()?;
            if meta.len() > 0 {
                return Ok(dst)
            }
        }
        let mut dst = self.cache_path.open_rw(path, self.config, &filename)?;
        let meta = dst.file().metadata()?;
        if meta.len() > 0 {
            return Ok(dst)
        }
        self.config.shell().status("Downloading", pkg)?;

        let config = self.config()?.unwrap();
        let mut url = config.dl.to_url()?;
        url.path_segments_mut().unwrap()
            .push(pkg.name())
            .push(&pkg.version().to_string())
            .push("download");

        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        let url = url.to_string();
        let mut handle = self.easy()?.borrow_mut();
        handle.get(true)?;
        handle.url(&url)?;
        handle.follow_location(true)?;
        let mut state = Sha256::new();
        let mut body = Vec::new();
        network::with_retry(self.config, || {
            state = Sha256::new();
            body = Vec::new();
            {
                let mut handle = handle.transfer();
                handle.write_function(|buf| {
                    state.update(buf);
                    body.extend_from_slice(buf);
                    Ok(buf.len())
                })?;
                handle.perform()?;
            }
            let code = handle.response_code()?;
            if code != 200 && code != 0 {
                let url = handle.effective_url()?.unwrap_or(&url);
                Err(HttpError::Not200(code, url.to_string()))
            } else {
                Ok(())
            }
        })?;

        // Verify what we just downloaded
        if state.finish().to_hex() != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        dst.write_all(&body)?;
        dst.seek(SeekFrom::Start(0))?;
        Ok(dst)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}

/// Updating the index is done pretty regularly so we want it to be as fast as
/// possible. For registries hosted on github (like the crates.io index) there's
/// a fast path available to use [1] to tell us that there's no updates to be
/// made.
///
/// This function will attempt to hit that fast path and verify that the `oid`
/// is actually the current `master` branch of the repository. If `true` is
/// returned then no update needs to be performed, but if `false` is returned
/// then the standard update logic still needs to happen.
///
/// [1]: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
///
/// Note that this function should never cause an actual failure because it's
/// just a fast path. As a result all errors are ignored in this function and we
/// just return a `bool`. Any real errors will be reported through the normal
/// update path above.
fn github_up_to_date(handle: &mut Easy, url: &Url, oid: &git2::Oid) -> bool {
    macro_rules! try {
        ($e:expr) => (match $e {
            Some(e) => e,
            None => return false,
        })
    }

    // This expects github urls in the form `github.com/user/repo` and nothing
    // else
    let mut pieces = try!(url.path_segments());
    let username = try!(pieces.next());
    let repo = try!(pieces.next());
    if pieces.next().is_some() {
        return false
    }

    let url = format!("https://api.github.com/repos/{}/{}/commits/master",
                      username, repo);
    try!(handle.get(true).ok());
    try!(handle.url(&url).ok());
    try!(handle.useragent("cargo").ok());
    let mut headers = List::new();
    try!(headers.append("Accept: application/vnd.github.3.sha").ok());
    try!(headers.append(&format!("If-None-Match: \"{}\"", oid)).ok());
    try!(handle.http_headers(headers).ok());
    try!(handle.perform().ok());

    try!(handle.response_code().ok()) == 304
}
<commit_msg>Fix a flaky concurrent test with correct locking<commit_after>use std::cell::{RefCell, Ref, Cell};
use std::io::SeekFrom;
use std::io::prelude::*;
use std::mem;
use std::path::Path;

use curl::easy::{Easy, List};
use git2;
use rustc_serialize::hex::ToHex;
use serde_json;
use url::Url;

use core::{PackageId, SourceId};
use ops;
use sources::git;
use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
use util::network;
use util::{FileLock, Filesystem, LazyCell};
use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl};
use util::errors::HttpError;

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    handle: LazyCell<RefCell<Easy>>,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str)
               -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config: config,
            tree: RefCell::new(None),
            handle: LazyCell::new(),
            repo: LazyCell::new(),
            head: Cell::new(None),
        }
    }

    fn easy(&self) -> CargoResult<&RefCell<Easy>> {
        self.handle.get_or_try_init(|| {
            ops::http_handle(self.config).map(RefCell::new)
        })
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.get_or_try_init(|| {
            let path = self.index_path.clone().into_path_unlocked();

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                return Ok(repo)
            }

            // Ok, now we need to lock and try the whole thing over again.
            let lock = self.index_path.open_rw(Path::new(INDEX_LOCK),
                                               self.config,
                                               "the registry index")?;
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    let _ = lock.remove_siblings();
                    Ok(git2::Repository::init_bare(&path)?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let oid = self.repo()?.refname_to_id("refs/remotes/origin/master")?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<git2::Tree>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()))
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe {
            mem::transmute::<git2::Tree, git2::Tree<'static>>(tree)
        };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn load(&self, _root: &Path, path: &Path) -> CargoResult<Vec<u8>> {
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        let repo = self.repo()?;
        let tree = self.tree()?;
        let entry = tree.get_path(path)?;
        let object = entry.to_object(&repo)?;
        let blob = match object.as_blob() {
            Some(blob) => blob,
            None => bail!("path `{}` is not a blob in the git repo", path.display()),
        };
        Ok(blob.content().to_vec())
    }

    fn config(&mut self) -> CargoResult<Option<RegistryConfig>> {
        self.repo()?; // create intermediate dirs and initialize the repo
        let _lock = self.index_path.open_ro(Path::new(INDEX_LOCK),
                                            self.config,
                                            "the registry index")?;
        let json = self.load(Path::new(""), Path::new("config.json"))?;
        let config = serde_json::from_slice(&json)?;
        Ok(Some(config))
    }

    fn update_index(&mut self) -> CargoResult<()> {
        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        ops::http_handle(self.config)?;

        let repo = self.repo()?;
        let _lock = self.index_path.open_rw(Path::new(INDEX_LOCK),
                                            self.config,
                                            "the registry index")?;
        self.config.shell().status("Updating",
             format!("registry `{}`", self.source_id.url()))?;
        let mut needs_fetch = true;

        if self.source_id.url().host_str() == Some("github.com") {
            if let Ok(oid) = self.head() {
                let mut handle = self.easy()?.borrow_mut();
                debug!("attempting github fast path for {}",
                       self.source_id.url());
                if github_up_to_date(&mut handle, self.source_id.url(), &oid) {
                    needs_fetch = false;
                } else {
                    debug!("fast path failed, falling back to a git fetch");
                }
            }
        }

        if needs_fetch {
            // git fetch origin master
            let url = self.source_id.url().to_string();
            let refspec = "refs/heads/master:refs/remotes/origin/master";
            git::fetch(&repo, &url, refspec, self.config).chain_error(|| {
                human(format!("failed to fetch `{}`", url))
            })?;
        }
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str)
                -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        // Attempt to open an read-only copy first to avoid an exclusive write
        // lock and also work with read-only filesystems. Note that we check the
        // length of the file like below to handle interrupted downloads.
        //
        // If this fails then we fall through to the exclusive path where we may
        // have to redownload the file.
        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            let meta = dst.file().metadata()?;
            if meta.len() > 0 {
                return Ok(dst)
            }
        }
        let mut dst = self.cache_path.open_rw(path, self.config, &filename)?;
        let meta = dst.file().metadata()?;
        if meta.len() > 0 {
            return Ok(dst)
        }
        self.config.shell().status("Downloading", pkg)?;

        let config = self.config()?.unwrap();
        let mut url = config.dl.to_url()?;
        url.path_segments_mut().unwrap()
            .push(pkg.name())
            .push(&pkg.version().to_string())
            .push("download");

        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        let url = url.to_string();
        let mut handle = self.easy()?.borrow_mut();
        handle.get(true)?;
        handle.url(&url)?;
        handle.follow_location(true)?;
        let mut state = Sha256::new();
        let mut body = Vec::new();
        network::with_retry(self.config, || {
            state = Sha256::new();
            body = Vec::new();
            {
                let mut handle = handle.transfer();
                handle.write_function(|buf| {
                    state.update(buf);
                    body.extend_from_slice(buf);
                    Ok(buf.len())
                })?;
                handle.perform()?;
            }
            let code = handle.response_code()?;
            if code != 200 && code != 0 {
                let url = handle.effective_url()?.unwrap_or(&url);
                Err(HttpError::Not200(code, url.to_string()))
            } else {
                Ok(())
            }
        })?;

        // Verify what we just downloaded
        if state.finish().to_hex() != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        dst.write_all(&body)?;
        dst.seek(SeekFrom::Start(0))?;
        Ok(dst)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}

/// Updating the index is done pretty regularly so we want it to be as fast as
/// possible. For registries hosted on github (like the crates.io index) there's
/// a fast path available to use [1] to tell us that there's no updates to be
/// made.
///
/// This function will attempt to hit that fast path and verify that the `oid`
/// is actually the current `master` branch of the repository. If `true` is
/// returned then no update needs to be performed, but if `false` is returned
/// then the standard update logic still needs to happen.
///
/// [1]: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
///
/// Note that this function should never cause an actual failure because it's
/// just a fast path. As a result all errors are ignored in this function and we
/// just return a `bool`. Any real errors will be reported through the normal
/// update path above.
fn github_up_to_date(handle: &mut Easy, url: &Url, oid: &git2::Oid) -> bool {
    macro_rules! try {
        ($e:expr) => (match $e {
            Some(e) => e,
            None => return false,
        })
    }

    // This expects github urls in the form `github.com/user/repo` and nothing
    // else
    let mut pieces = try!(url.path_segments());
    let username = try!(pieces.next());
    let repo = try!(pieces.next());
    if pieces.next().is_some() {
        return false
    }

    let url = format!("https://api.github.com/repos/{}/{}/commits/master",
                      username, repo);
    try!(handle.get(true).ok());
    try!(handle.url(&url).ok());
    try!(handle.useragent("cargo").ok());
    let mut headers = List::new();
    try!(headers.append("Accept: application/vnd.github.3.sha").ok());
    try!(headers.append(&format!("If-None-Match: \"{}\"", oid)).ok());
    try!(handle.http_headers(headers).ok());
    try!(handle.perform().ok());

    try!(handle.response_code().ok()) == 304
}
<|endoftext|>


<commit_before>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let current_version = self.current_version();
        if let (Some(c), Some(i)) = (current_version, index_version) {
            if c.ends_with(i) {
                return Poll::Ready(Ok(LoadResponse::CacheValid));
            }
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            current_version: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let file_version = entry.id().to_string();
            if let Some(c) = current_version {
                if c.starts_with(&file_version) {
                    return Ok(LoadResponse::CacheValid);
                }
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: current_version.map(String::from),
                // TODO: When the reading code has been stable for long enough (Say 7/2022)
                // change to `file_version + ":" + current_version`
            })
        }

        match load_helper(&self, path, current_version.as_deref()) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<commit_msg>backwards ends_with<commit_after>use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::Poll;

/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
/// downloaded as needed and cached locally.
pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`).
    cache_path: Filesystem,
    source_id: SourceId,
    index_git_ref: GitReference,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
    current_sha: Cell<Option<InternedString>>,
    needs_update: bool, // Does this registry need to be updated?
    updated: bool,      // Has this registry been updated this session?
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id,
            config,
            // TODO: we should probably make this configurable
            index_git_ref: GitReference::DefaultBranch,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
            current_sha: Cell::new(None),
            needs_update: false,
            updated: false,
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.config.assert_package_cache_locked(&self.index_path);

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    drop(paths::remove_dir_all(&path));
                    paths::create_dir_all(&path)?;

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).with_context(|| {
                        format!("failed to initialize index git repository (in {:?})", path)
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let repo = self.repo()?;
            let oid = self.index_git_ref.resolve(repo)?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree<'_>, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }

    fn current_version(&self) -> Option<InternedString> {
        if let Some(sha) = self.current_sha.get() {
            return Some(sha);
        }
        let sha = InternedString::new(&self.head().ok()?.to_string());
        self.current_sha.set(Some(sha));
        Some(sha)
    }
}

const LAST_UPDATED_FILE: &str = ".last-updated";

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
        self.config.assert_package_cache_locked(path)
    }

    fn load(
        &mut self,
        _root: &Path,
        path: &Path,
        index_version: Option<&str>,
    ) -> Poll<CargoResult<LoadResponse>> {
        if self.needs_update {
            return Poll::Pending;
        }
        // Check if the cache is valid.
        let current_version = self.current_version();
        if let (Some(c), Some(i)) = (current_version, index_version) {
            if i.ends_with(c.as_str()) {
                return Poll::Ready(Ok(LoadResponse::CacheValid));
            }
        }
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        fn load_helper(
            registry: &RemoteRegistry<'_>,
            path: &Path,
            current_version: Option<&str>,
        ) -> CargoResult<LoadResponse> {
            let repo = registry.repo()?;
            let tree = registry.tree()?;
            let entry = tree.get_path(path);
            let entry = entry?;
            let file_version = entry.id().to_string();
            if let Some(c) = current_version {
                if c.starts_with(&file_version) {
                    return Ok(LoadResponse::CacheValid);
                }
            }

            let object = entry.to_object(repo)?;
            let blob = match object.as_blob() {
                Some(blob) => blob,
                None => anyhow::bail!("path `{}` is not a blob in the git repo", path.display()),
            };

            Ok(LoadResponse::Data {
                raw_data: blob.content().to_vec(),
                index_version: current_version.map(String::from),
                // TODO: When the reading code has been stable for long enough (Say 7/2022)
                // change to `file_version + ":" + current_version`
            })
        }

        match load_helper(&self, path, current_version.as_deref()) {
            Ok(result) => Poll::Ready(Ok(result)),
            Err(_) if !self.updated => {
                // If git returns an error and we haven't updated the repo, return
                // pending to allow an update to try again.
                self.needs_update = true;
                Poll::Pending
            }
            Err(e)
                if e.downcast_ref::<git2::Error>()
                    .map(|e| e.code() == git2::ErrorCode::NotFound)
                    .unwrap_or_default() =>
            {
                // The repo has been updated and the file does not exist.
                Poll::Ready(Ok(LoadResponse::NotFound))
            }
            Err(e) => Poll::Ready(Err(e)),
        }
    }

    fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
        debug!("loading config");
        self.prepare()?;
        self.config.assert_package_cache_locked(&self.index_path);
        match self.load(Path::new(""), Path::new("config.json"), None)? {
            Poll::Ready(LoadResponse::Data { raw_data, .. }) => {
                trace!("config loaded");
                Poll::Ready(Ok(Some(serde_json::from_slice(&raw_data)?)))
            }
            Poll::Ready(_) => Poll::Ready(Ok(None)),
            Poll::Pending => Poll::Pending,
        }
    }

    fn block_until_ready(&mut self) -> CargoResult<()> {
        if !self.needs_update {
            return Ok(());
        }

        self.updated = true;
        self.needs_update = false;

        if self.config.offline() {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }
        // Make sure the index is only updated once per session since it is an
        // expensive operation. This generally only happens when the resolver
        // is run multiple times, such as during `cargo publish`.
        if self.config.updated_sources().contains(&self.source_id) {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        self.current_sha.set(None);
        let path = self.config.assert_package_cache_locked(&self.index_path);
        self.config
            .shell()
            .status("Updating", self.source_id.display_index())?;

        // Fetch the latest version of our `index_git_ref` into the index
        // checkout.
        let url = self.source_id.url();
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url.as_str(), &self.index_git_ref, self.config)
            .with_context(|| format!("failed to fetch `{}`", url))?;
        self.config.updated_sources().insert(self.source_id);

        // Create a dummy file to record the mtime for when we updated the
        // index.
        paths::create(&path.join(LAST_UPDATED_FILE))?;

        Ok(())
    }

    fn invalidate_cache(&mut self) {
        if !self.updated {
            self.needs_update = true;
        }
    }

    fn is_updated(&self) -> bool {
        self.updated
    }

    fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> {
        let registry_config = loop {
            match self.config()? {
                Poll::Pending => self.block_until_ready()?,
                Poll::Ready(cfg) => break cfg.unwrap(),
            }
        };

        download::download(
            &self.cache_path,
            &self.config,
            pkg,
            checksum,
            registry_config,
        )
    }

    fn finish_download(
        &mut self,
        pkg: PackageId,
        checksum: &str,
        data: &[u8],
    ) -> CargoResult<File> {
        download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
    }

    fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
        download::is_crate_downloaded(&self.cache_path, &self.config, pkg)
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use cargo_edit::{
    colorize_stderr, resolve_manifests, upgrade_requirement, workspace_members, LocalManifest,
};
use clap::Args;
use termcolor::{BufferWriter, Color, ColorSpec, WriteColor};

use crate::errors::*;
use crate::version::BumpLevel;
use crate::version::TargetVersion;

/// Change a package's version in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[command(version)]
#[command(group = clap::ArgGroup::new("ver").multiple(false))]
pub struct VersionArgs {
    /// Version to change manifests to
    #[arg(group = "ver")]
    target: Option<semver::Version>,

    /// Increment manifest version
    #[arg(long, group = "ver")]
    bump: Option<BumpLevel>,

    /// Specify the version metadata field (e.g. a wrapped libraries version)
    #[arg(short, long)]
    pub metadata: Option<String>,

    /// Path to the manifest to upgrade
    #[arg(long, value_name = "PATH")]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to change the version of.
    #[arg(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Option<String>,

    /// Modify all packages in the workspace.
    #[arg(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Modify all packages in the workspace.
    #[arg(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[arg(long)]
    dry_run: bool,

    /// Crates to exclude and not modify.
    #[arg(long)]
    exclude: Vec<String>,

    /// Unstable (nightly-only) flags
    #[arg(short = 'Z', value_name = "FLAG", global = true, value_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl VersionArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: VersionArgs) -> CargoResult<()> {
    let VersionArgs {
        target,
        bump,
        metadata,
        manifest_path,
        pkgid,
        all,
        dry_run,
        workspace,
        exclude,
        unstable_features: _,
    } = args;

    let target = match (target, bump) {
        (None, None) => TargetVersion::Relative(BumpLevel::Release),
        (None, Some(level)) => TargetVersion::Relative(level),
        (Some(version), None) => TargetVersion::Absolute(version),
        (Some(_), Some(_)) => unreachable!("clap groups should prevent this"),
    };

    if all {
        deprecated_message("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }
    let all = workspace || all;
    let manifests = Manifests(resolve_manifests(
        manifest_path.as_deref(),
        all,
        pkgid.as_deref().into_iter().collect::<Vec<_>>(),
    )?);

    if dry_run {
        dry_run_message()?;
    }

    let workspace_members = workspace_members(manifest_path.as_deref())?;

    for package in manifests.0 {
        if exclude.contains(&package.name) {
            continue;
        }
        let current = &package.version;
        let next = target.bump(current, metadata.as_deref())?;
        if let Some(next) = next {
            {
                let mut manifest = LocalManifest::try_new(Path::new(&package.manifest_path))?;
                manifest.set_package_version(&next);

                upgrade_message(package.name.as_str(), current, &next)?;
                if !dry_run {
                    manifest.write()?;
                }
            }

            let crate_root =
                dunce::canonicalize(package.manifest_path.parent().expect("at least a parent"))?;
            for member in workspace_members.iter() {
                let mut dep_manifest = LocalManifest::try_new(member.manifest_path.as_std_path())?;
                let mut changed = false;
                let dep_crate_root = dep_manifest
                    .path
                    .parent()
                    .expect("at least a parent")
                    .to_owned();
                for dep in dep_manifest
                    .get_dependency_tables_mut()
                    .flat_map(|t| t.iter_mut().filter_map(|(_, d)| d.as_table_like_mut()))
                    .filter(|d| {
                        if !d.contains_key("version") {
                            return false;
                        }
                        match d.get("path").and_then(|i| i.as_str()).and_then(|relpath| {
                            dunce::canonicalize(dep_crate_root.join(relpath)).ok()
                        }) {
                            Some(dep_path) => dep_path == crate_root.as_path(),
                            None => false,
                        }
                    })
                {
                    let old_req = dep
                        .get("version")
                        .expect("filter ensures this")
                        .as_str()
                        .unwrap_or("*");
                    if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                        upgrade_dependent_message(member.name.as_str(), old_req, &new_req)?;
                        dep.insert("version", toml_edit::value(new_req));
                        changed = true;
                    }
                }
                if changed && !dry_run {
                    dep_manifest.write()?;
                }
            }
        }
    }

    Ok(())
}

/// A collection of manifests.
struct Manifests(Vec<cargo_metadata::Package>);

fn dry_run_message() -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    write!(&mut buffer, "Starting dry run. ").with_context(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    writeln!(&mut buffer, "Changes will not be saved.")
        .with_context(|| "Failed to write dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn deprecated_message(message: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    writeln!(&mut buffer, "{}", message).with_context(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn upgrade_message(name: &str, from: &semver::Version, to: &semver::Version) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .with_context(|| "Failed to print dry run message")?;
    write!(&mut buffer, "{:>12}", "Upgraded").with_context(|| "Failed to print dry run message")?;
    buffer
        .reset()
        .with_context(|| "Failed to print dry run message")?;
    writeln!(&mut buffer, " {} from {} to {}", name, from, to)
        .with_context(|| "Failed to print dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn upgrade_dependent_message(name: &str, old_req: &str, new_req: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .with_context(|| "Failed to print dry run message")?;
    write!(&mut buffer, "{:>16}", "Updated dependency")
        .with_context(|| "Failed to print dry run message")?;
    buffer
        .reset()
        .with_context(|| "Failed to print dry run message")?;
    writeln!(&mut buffer, " {} from {} to {}", name, old_req, new_req)
        .with_context(|| "Failed to print dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}
<commit_msg>refactor(version): Remove unused Manifests<commit_after>use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use cargo_edit::{
    colorize_stderr, resolve_manifests, upgrade_requirement, workspace_members, LocalManifest,
};
use clap::Args;
use termcolor::{BufferWriter, Color, ColorSpec, WriteColor};

use crate::errors::*;
use crate::version::BumpLevel;
use crate::version::TargetVersion;

/// Change a package's version in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[command(version)]
#[command(group = clap::ArgGroup::new("ver").multiple(false))]
pub struct VersionArgs {
    /// Version to change manifests to
    #[arg(group = "ver")]
    target: Option<semver::Version>,

    /// Increment manifest version
    #[arg(long, group = "ver")]
    bump: Option<BumpLevel>,

    /// Specify the version metadata field (e.g. a wrapped libraries version)
    #[arg(short, long)]
    pub metadata: Option<String>,

    /// Path to the manifest to upgrade
    #[arg(long, value_name = "PATH")]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to change the version of.
    #[arg(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Option<String>,

    /// Modify all packages in the workspace.
    #[arg(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Modify all packages in the workspace.
    #[arg(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[arg(long)]
    dry_run: bool,

    /// Crates to exclude and not modify.
    #[arg(long)]
    exclude: Vec<String>,

    /// Unstable (nightly-only) flags
    #[arg(short = 'Z', value_name = "FLAG", global = true, value_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl VersionArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ValueEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: VersionArgs) -> CargoResult<()> {
    let VersionArgs {
        target,
        bump,
        metadata,
        manifest_path,
        pkgid,
        all,
        dry_run,
        workspace,
        exclude,
        unstable_features: _,
    } = args;

    let target = match (target, bump) {
        (None, None) => TargetVersion::Relative(BumpLevel::Release),
        (None, Some(level)) => TargetVersion::Relative(level),
        (Some(version), None) => TargetVersion::Absolute(version),
        (Some(_), Some(_)) => unreachable!("clap groups should prevent this"),
    };

    if all {
        deprecated_message("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }
    let all = workspace || all;
    let manifests = resolve_manifests(
        manifest_path.as_deref(),
        all,
        pkgid.as_deref().into_iter().collect::<Vec<_>>(),
    )?;

    if dry_run {
        dry_run_message()?;
    }

    let workspace_members = workspace_members(manifest_path.as_deref())?;

    for package in manifests {
        if exclude.contains(&package.name) {
            continue;
        }
        let current = &package.version;
        let next = target.bump(current, metadata.as_deref())?;
        if let Some(next) = next {
            {
                let mut manifest = LocalManifest::try_new(Path::new(&package.manifest_path))?;
                manifest.set_package_version(&next);

                upgrade_message(package.name.as_str(), current, &next)?;
                if !dry_run {
                    manifest.write()?;
                }
            }

            let crate_root =
                dunce::canonicalize(package.manifest_path.parent().expect("at least a parent"))?;
            for member in workspace_members.iter() {
                let mut dep_manifest = LocalManifest::try_new(member.manifest_path.as_std_path())?;
                let mut changed = false;
                let dep_crate_root = dep_manifest
                    .path
                    .parent()
                    .expect("at least a parent")
                    .to_owned();
                for dep in dep_manifest
                    .get_dependency_tables_mut()
                    .flat_map(|t| t.iter_mut().filter_map(|(_, d)| d.as_table_like_mut()))
                    .filter(|d| {
                        if !d.contains_key("version") {
                            return false;
                        }
                        match d.get("path").and_then(|i| i.as_str()).and_then(|relpath| {
                            dunce::canonicalize(dep_crate_root.join(relpath)).ok()
                        }) {
                            Some(dep_path) => dep_path == crate_root.as_path(),
                            None => false,
                        }
                    })
                {
                    let old_req = dep
                        .get("version")
                        .expect("filter ensures this")
                        .as_str()
                        .unwrap_or("*");
                    if let Some(new_req) = upgrade_requirement(old_req, &next)? {
                        upgrade_dependent_message(member.name.as_str(), old_req, &new_req)?;
                        dep.insert("version", toml_edit::value(new_req));
                        changed = true;
                    }
                }
                if changed && !dry_run {
                    dep_manifest.write()?;
                }
            }
        }
    }

    Ok(())
}

fn dry_run_message() -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    write!(&mut buffer, "Starting dry run. ").with_context(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    writeln!(&mut buffer, "Changes will not be saved.")
        .with_context(|| "Failed to write dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn deprecated_message(message: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    writeln!(&mut buffer, "{}", message).with_context(|| "Failed to write dry run message")?;
    buffer
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn upgrade_message(name: &str, from: &semver::Version, to: &semver::Version) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .with_context(|| "Failed to print dry run message")?;
    write!(&mut buffer, "{:>12}", "Upgraded").with_context(|| "Failed to print dry run message")?;
    buffer
        .reset()
        .with_context(|| "Failed to print dry run message")?;
    writeln!(&mut buffer, " {} from {} to {}", name, from, to)
        .with_context(|| "Failed to print dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}

fn upgrade_dependent_message(name: &str, old_req: &str, new_req: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let bufwtr = BufferWriter::stderr(colorchoice);
    let mut buffer = bufwtr.buffer();
    buffer
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .with_context(|| "Failed to print dry run message")?;
    write!(&mut buffer, "{:>16}", "Updated dependency")
        .with_context(|| "Failed to print dry run message")?;
    buffer
        .reset()
        .with_context(|| "Failed to print dry run message")?;
    writeln!(&mut buffer, " {} from {} to {}", name, old_req, new_req)
        .with_context(|| "Failed to print dry run message")?;
    bufwtr
        .print(&buffer)
        .with_context(|| "Failed to print dry run message")
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! define_model {
    (
        // Name of model, e.g. `Jedi`
        $model:ident,
        $model_meta:ident,
        // Name of table-helper, e.g. `JediTable`
        $table:ident,
        // Name of table-helper `ManySelectQueryExt` trait e.g. `JediTableManySelectQueryExt`
        $many_select_query_ext:ident,
        // Name of table-helper `OneSelectQueryExt` trait e.g. `JediTableOneSelectQueryExt`
        $one_select_query_ext:ident,
        // Table name in database
        $table_name:expr,
        // Collection of fields
        [ $((
            $field_name:ident,
            $field_type:ty,
            $field_name_f:ident,
            $field_get:ident,
            $field_set:ident,
            $field_changed_flag:ident, // the name of internal flag field
            $field_changed_accessor:ident, // accessor name
            $($vis:tt)*)),+ ],

        [
            $($before_create:ident),*
        ],
        [
            $($before_save:ident),*
        ]
    ) => (
        #[deriving(Default, Show, Clone)]
        #[allow(dead_code)]
        pub struct $model {
            $(
                $field_name: Option<$field_type>,
            )+
            __meta: $model_meta
        }

        #[deriving(Default, Show, Clone)]
        #[allow(dead_code)]
        pub struct $model_meta {
            $(
                $field_changed_flag: bool,
            )+
            changed: bool,
        }

        impl $model_meta {
            pub fn new() -> $model_meta {
                $model_meta {
                    $(
                        // FIXME a macros issue don't allows to use `false` here
                        $field_changed_flag: !true,
                    )+
                    changed: false,
                }
            }
        }

        impl $model {

            $(
                #[allow(dead_code)]
                pub fn $field_get(&self) -> &$field_type {
                    return self.$field_name.as_ref().unwrap();
                }

                #[allow(dead_code)]
                pub fn $field_set(&mut self, value: $field_type) {
                    self.$field_name = Some(value);
                    self.__meta.changed = true;
                    self.__meta.$field_changed_flag = true;
                }

                #[allow(dead_code)]
                pub fn $field_changed_accessor(&self) -> bool {
                    self.__meta.$field_changed_flag
                }
            )+

            fn empty() -> $model {
                $model {
                   $(
                       $field_name: None,
                   )+
                   __meta: $model_meta::new()
                }
            }

            fn from_row<T, L>(query: &::deuterium::SelectQuery<T, L, $model>, row: &::postgres::Row) -> $model {
                match &query.select {
                    &::deuterium::SelectAll => {
                        $model {
                           $(
                                $field_name: Some(row.get(stringify!($field_name))),
                           )+
                           __meta: $model_meta::new()
                        }
                    },
                    &::deuterium::SelectOnly(_) => {
                        let mut model = $model::empty();
                        $(
                            model.$field_name = match row.get_opt(stringify!($field_name)) {
                                Ok(val) => Some(val),
                                Err(_) => None
                            };
                        )+

                        model
                    }
                }
            }
        }

        #[deriving(Clone)]
        pub struct $table(::deuterium::TableDef);

        #[allow(dead_code)]
        impl $model {

            pub fn table_name() -> &'static str {
                $table_name
            }

            pub fn table() -> $table {
                $table(::deuterium::TableDef::new($model::table_name()))
            }

            pub fn alias(alias: &str) -> $table {
                $table(::deuterium::TableDef::new_with_alias($model::table_name(), alias))
            }

            $(
                pub fn $field_name_f() -> ::deuterium::NamedField<$field_type> {
                    ::deuterium::NamedField::<$field_type>::new(stringify!($field_name), $model::table_name())
                }
            )+

            pub fn create(&mut self) -> ::deuterium::InsertQuery<(), (), $model, (), ()> {
                let query = {
                    let mut fields: Vec<&::deuterium::Field> = vec![];
                    let mut values: Vec<&::deuterium::ToExpression<()>> = vec![];

                    $(
                        $before_create(self);
                    )*

                    $(
                        $before_save(self);
                    )*

                    $(
                        let $field_name;
                        if self.__meta.$field_changed_flag == true {
                            $field_name = $model::$field_name_f();
                            fields.push(&$field_name);
                            values.push(self.$field_get());
                        }
                    )+

                    let mut query = $model::table().insert_fields(fields.as_slice());
                    query.push_untyped(values.as_slice());
                    query
                };

                $(
                    if self.__meta.$field_changed_flag == true {
                        self.__meta.$field_changed_flag = false;
                    }
                )+

                query
            }

            pub fn update(&mut self) -> ::deuterium::UpdateQuery<(), ::deuterium::NoResult, $model> {

                $(
                    $before_save(self);
                )*

                let mut query = $model::table().update();

                $(
                    if self.__meta.$field_changed_flag == true {
                        let field = $model::$field_name_f().set(self.$field_get());
                        query = query.field(field);
                        self.__meta.$field_changed_flag = false;
                    }
                )+

                query.where_(self.lookup_predicate())
            }

            pub fn delete(&mut self) -> ::deuterium::DeleteQuery<(), ::deuterium::NoResult, $model> {
                $model::table().delete().where_(self.lookup_predicate())
            }

        }

        impl ::deuterium::Table for $table {
            fn upcast_table(&self) -> ::deuterium::RcTable {
                ::std::sync::Arc::new(box self.clone() as ::deuterium::BoxedTable)
            }

            fn get_table_name(&self) -> &String {
                self.0.get_table_name()
            }

            fn get_table_alias(&self) -> &Option<String> {
                self.0.get_table_alias()
            }
        }

        #[allow(dead_code)]
        impl $table {
            $(
                pub fn $field_name_f(&self) -> ::deuterium::NamedField<$field_type> {
                    ::deuterium::NamedField::<$field_type>::field_of(stringify!($field_name), self)
                }
            )+
        }

        impl ::deuterium::From for $table {
            fn as_sql(&self) -> &::deuterium::FromToSql {
                &self.0
            }

            fn upcast_from(&self) -> ::deuterium::RcFrom {
                ::std::sync::Arc::new(box self.clone() as ::deuterium::BoxedFrom)
            }
        }

        impl ::deuterium::Selectable<$model> for $table { }
        impl ::deuterium::Updatable<$model> for $table { }
        impl ::deuterium::Deletable<$model> for $table { }
        impl ::deuterium::Insertable<$model> for $table { }

        // SelectQuery extension
        pub trait $many_select_query_ext<T>: ::deuterium::QueryToSql {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model>;

            fn query_list(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Vec<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().map(|row| {
                        $model::from_row(self.as_model_select_query(), &row)
                    }).collect()
                })
            }
        }

        // SelectQuery extension
        pub trait $one_select_query_ext<T>: ::deuterium::QueryToSql {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model>;

            fn query_list(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Vec<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().map(|row| {
                        $model::from_row(self.as_model_select_query(), &row)
                    }).collect()
                })
            }

            fn query(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Option<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().next().map(|row| $model::from_row(self.as_model_select_query(), &row))
                })
            }
        }

        impl<T> $many_select_query_ext<T> for ::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model> {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model> {
                self
            }
        }

        impl<T> $one_select_query_ext<T> for ::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model> {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model> {
                self
            }
        }
    )
}

#[macro_export]
macro_rules! primary_key(
    ($s:ident, $model:ident, $body:block) => (
        impl $model {
            #[allow(dead_code)]
            pub fn lookup_predicate(&$s) -> ::deuterium::RcPredicate {
                $body
            }
        }
        // TODO lookup_predicate
        // TODO get_primary()
        // TODO get_primary_f()
    )
)<commit_msg>feat: write some words about what is going on in generator<commit_after>#[macro_export]
macro_rules! define_model {
    (
        // Name of model, e.g. `Jedi`
        $model:ident,
        $model_meta:ident,
        // Name of table-helper, e.g. `JediTable`
        $table:ident,
        // Name of table-helper `ManySelectQueryExt` trait e.g. `JediTableManySelectQueryExt`
        $many_select_query_ext:ident,
        // Name of table-helper `OneSelectQueryExt` trait e.g. `JediTableOneSelectQueryExt`
        $one_select_query_ext:ident,
        // Table name in database
        $table_name:expr,
        // Collection of fields
        [ $((
            $field_name:ident, // Field name, e.g. id
            $field_type:ty, // Field type, e.g. Uuid
            $field_name_f:ident, // Own field getter name, e.g. `id_f`
            $field_get:ident,  // Field's value getter name
            $field_set:ident,  // Field's value setter name
            $field_changed_flag:ident, // the name of internal flag field
            $field_changed_accessor:ident, // accessor name
            $($vis:tt)*)),+ // Hacky field visibility (not usable for now)
        ],

        [
            $($before_create:ident),*
        ],
        [
            $($before_save:ident),*
        ]
    ) => (

        // Generate Meta struct to hold dirty flags and other helpful internal state.

        #[deriving(Default, Show, Clone)]
        #[allow(dead_code)]
        pub struct $model_meta {
            $(
                $field_changed_flag: bool,
            )+
            changed: bool,
        }

        impl $model_meta {
            pub fn new() -> $model_meta {
                $model_meta {
                    $(
                        // FIXME a macros issue don't allows to use `false` here
                        $field_changed_flag: !true,
                    )+
                    changed: false,
                }
            }
        }

        // Generate new model struct with all fields of type Option<$field_type>,
        // where None case we need to tell that field was not loaded from database.
        // New struct also will include __meta field where internal model state is stored.

        #[deriving(Default, Show, Clone)]
        #[allow(dead_code)]
        pub struct $model {
            $(
                $field_name: Option<$field_type>,
            )+
            __meta: $model_meta
        }

        impl $model {

            // Generate a set of methods to deal with fields values and fields dirty state.
            // We generate a getter, a setter and an accessor for each defined field.

            $(
                #[allow(dead_code)]
                pub fn $field_get(&self) -> &$field_type {
                    return self.$field_name.as_ref().unwrap();
                }

                #[allow(dead_code)]
                pub fn $field_set(&mut self, value: $field_type) {
                    self.$field_name = Some(value);
                    self.__meta.changed = true;
                    self.__meta.$field_changed_flag = true;
                }

                #[allow(dead_code)]
                pub fn $field_changed_accessor(&self) -> bool {
                    self.__meta.$field_changed_flag
                }
            )+

            // Generate method to create empty model instance. `Empty` here means that all the fields
            // are in undefined state and all dirty bits are disabled.

            fn empty() -> $model {
                $model {
                   $(
                       $field_name: None,
                   )+
                   __meta: $model_meta::new()
                }
            }

            // Very helpful stuff to unwrap Model instance from database Row.

            fn from_row<T, L>(query: &::deuterium::SelectQuery<T, L, $model>, row: &::postgres::Row) -> $model {
                match &query.select {
                    &::deuterium::SelectAll => {
                        $model {
                           $(
                                $field_name: Some(row.get(stringify!($field_name))),
                           )+
                           __meta: $model_meta::new()
                        }
                    },
                    &::deuterium::SelectOnly(_) => {
                        let mut model = $model::empty();
                        $(
                            model.$field_name = match row.get_opt(stringify!($field_name)) {
                                Ok(val) => Some(val),
                                Err(_) => None
                            };
                        )+

                        model
                    }
                }
            }
        }

        // We also generate ModelTable struct to deal with requests.

        #[deriving(Clone)]
        pub struct $table(::deuterium::TableDef);

        #[allow(dead_code)]
        impl $model {

            pub fn table_name() -> &'static str {
                $table_name
            }

            pub fn table() -> $table {
                $table(::deuterium::TableDef::new($model::table_name()))
            }

            pub fn alias(alias: &str) -> $table {
                $table(::deuterium::TableDef::new_with_alias($model::table_name(), alias))
            }

            $(
                pub fn $field_name_f() -> ::deuterium::NamedField<$field_type> {
                    ::deuterium::NamedField::<$field_type>::new(stringify!($field_name), $model::table_name())
                }
            )+

            pub fn create(&mut self) -> ::deuterium::InsertQuery<(), (), $model, (), ()> {
                let query = {
                    let mut fields: Vec<&::deuterium::Field> = vec![];
                    let mut values: Vec<&::deuterium::ToExpression<()>> = vec![];

                    $(
                        $before_create(self);
                    )*

                    $(
                        $before_save(self);
                    )*

                    $(
                        let $field_name;
                        if self.__meta.$field_changed_flag == true {
                            $field_name = $model::$field_name_f();
                            fields.push(&$field_name);
                            values.push(self.$field_get());
                        }
                    )+

                    let mut query = $model::table().insert_fields(fields.as_slice());
                    query.push_untyped(values.as_slice());
                    query
                };

                $(
                    if self.__meta.$field_changed_flag == true {
                        self.__meta.$field_changed_flag = false;
                    }
                )+

                query
            }

            pub fn update(&mut self) -> ::deuterium::UpdateQuery<(), ::deuterium::NoResult, $model> {

                $(
                    $before_save(self);
                )*

                let mut query = $model::table().update();

                $(
                    if self.__meta.$field_changed_flag == true {
                        let field = $model::$field_name_f().set(self.$field_get());
                        query = query.field(field);
                        self.__meta.$field_changed_flag = false;
                    }
                )+

                query.where_(self.lookup_predicate())
            }

            pub fn delete(&mut self) -> ::deuterium::DeleteQuery<(), ::deuterium::NoResult, $model> {
                $model::table().delete().where_(self.lookup_predicate())
            }

        }

        impl ::deuterium::Table for $table {
            fn upcast_table(&self) -> ::deuterium::RcTable {
                ::std::sync::Arc::new(box self.clone() as ::deuterium::BoxedTable)
            }

            fn get_table_name(&self) -> &String {
                self.0.get_table_name()
            }

            fn get_table_alias(&self) -> &Option<String> {
                self.0.get_table_alias()
            }
        }

        #[allow(dead_code)]
        impl $table {
            $(
                pub fn $field_name_f(&self) -> ::deuterium::NamedField<$field_type> {
                    ::deuterium::NamedField::<$field_type>::field_of(stringify!($field_name), self)
                }
            )+
        }

        impl ::deuterium::From for $table {
            fn as_sql(&self) -> &::deuterium::FromToSql {
                &self.0
            }

            fn upcast_from(&self) -> ::deuterium::RcFrom {
                ::std::sync::Arc::new(box self.clone() as ::deuterium::BoxedFrom)
            }
        }

        // Implement an ability to produce typed Deuterium queries.

        impl ::deuterium::Selectable<$model> for $table { }
        impl ::deuterium::Updatable<$model> for $table { }
        impl ::deuterium::Deletable<$model> for $table { }
        impl ::deuterium::Insertable<$model> for $table { }

        // Generate an extensions to SelectQuery. These extensions provide query_ methods
        // that allows to execute queries in database and unwrap results to an Iterator, a Vec or just an
        // instance of Model.

        // SelectQuery extension
        pub trait $many_select_query_ext<T>: ::deuterium::QueryToSql {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model>;

            fn query_list(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Vec<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().map(|row| {
                        $model::from_row(self.as_model_select_query(), &row)
                    }).collect()
                })
            }
        }

        // SelectQuery extension
        pub trait $one_select_query_ext<T>: ::deuterium::QueryToSql {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model>;

            fn query_list(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Vec<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().map(|row| {
                        $model::from_row(self.as_model_select_query(), &row)
                    }).collect()
                })
            }

            fn query(&self, cn: &::postgres::Connection, params: &[&::postgres::types::ToSql]) -> Option<$model> {
                query_pg!(self, cn, params, rows, {
                    rows.unwrap().next().map(|row| $model::from_row(self.as_model_select_query(), &row))
                })
            }
        }

        impl<T> $many_select_query_ext<T> for ::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model> {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitMany, $model> {
                self
            }
        }

        impl<T> $one_select_query_ext<T> for ::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model> {
            fn as_model_select_query(&self) -> &::deuterium::SelectQuery<T, ::deuterium::LimitOne, $model> {
                self
            }
        }
    )
}

#[macro_export]
macro_rules! primary_key(
    ($s:ident, $model:ident, $body:block) => (
        impl $model {
            #[allow(dead_code)]
            pub fn lookup_predicate(&$s) -> ::deuterium::RcPredicate {
                $body
            }
        }
        // TODO get_primary()
        // TODO get_primary_f()
    )
)<|endoftext|>