1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Defines a package registry that can be defined and modified entirely in
//! memory. It's useful for creating exact conditions for test cases for
//! resolution, installation, upgrading, etc.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use anyhow::format_err;

use crate::{
    manifest::Manifest, package_id::PackageId, package_req::PackageReq,
    package_source::PackageSource, test_package::PackageBuilder,
};

use super::PackageContents;

/// An in-memory registry that can have packages published to it.
///
/// `InMemoryRegistry` itself is not a `PackageSource`, but one can be created
/// by calling `source()`.
pub struct InMemoryRegistry {
    storage: Storage,
}

impl InMemoryRegistry {
    pub fn new() -> Self {
        Self {
            storage: Default::default(),
        }
    }

    /// Publish a new package to the registry.
    pub fn publish(&self, builder: PackageBuilder) {
        let mut storage = self.storage.contents.borrow_mut();
        let (manifest, contents) = builder.package();

        let scope = storage
            .entry(manifest.package.name.scope().to_owned())
            .or_default();

        let entries = scope
            .entry(manifest.package.name.name().to_owned())
            .or_default();

        entries.push(PackageEntry { manifest, contents });
    }

    /// Returns a handle to an object that can be used as a `PackageSource`.
    pub fn source(&self) -> InMemoryRegistrySource {
        InMemoryRegistrySource {
            storage: self.storage.clone(),
        }
    }
}

/// Returned by `InMemoryRegistry::source` and can be passed to package
/// resolution code in order to tell it to use this package registry.
pub struct InMemoryRegistrySource {
    storage: Storage,
}

impl PackageSource for InMemoryRegistrySource {
    fn update(&self) -> anyhow::Result<()> {
        Ok(())
    }

    fn query(&self, package_req: &PackageReq) -> anyhow::Result<Vec<Manifest>> {
        let storage = self.storage.contents.borrow();
        let scope = match storage.get(package_req.name().scope()) {
            Some(scope) => scope,
            None => return Ok(Vec::new()),
        };

        let entries = match scope.get(package_req.name().name()) {
            Some(entries) => entries,
            None => return Ok(Vec::new()),
        };

        let result = entries
            .iter()
            .filter(|entry| {
                package_req
                    .version_req()
                    .matches(&entry.manifest.package.version)
            })
            .map(|entry| &entry.manifest)
            .cloned()
            .collect();

        Ok(result)
    }

    fn download_package(&self, package_id: &PackageId) -> anyhow::Result<PackageContents> {
        let storage = self.storage.contents.borrow();
        let scope = storage
            .get(package_id.name().scope())
            .ok_or_else(|| format_err!("Package {} does not exist", package_id))?;

        let manifests = scope
            .get(package_id.name().name())
            .ok_or_else(|| format_err!("Package {} does not exist", package_id))?;

        let entry = manifests
            .iter()
            .find(|entry| &entry.manifest.package.version == package_id.version())
            .ok_or_else(|| format_err!("Package {} does not exist", package_id))?;

        Ok(entry.contents.clone())
    }
}

struct PackageEntry {
    manifest: Manifest,
    contents: PackageContents,
}

#[derive(Clone, Default)]
struct Storage {
    contents: Rc<RefCell<HashMap<String, HashMap<String, Vec<PackageEntry>>>>>,
}