aboutsummaryrefslogtreecommitdiff
path: root/src/download.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/download.rs')
-rw-r--r--src/download.rs161
1 files changed, 0 insertions, 161 deletions
diff --git a/src/download.rs b/src/download.rs
deleted file mode 100644
index b9507b7..0000000
--- a/src/download.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-//! Module to download mods from the internet.
-//!
-//! This module allows to download mods from various sources in the internet. Source specification
-//! is done through the [`Source`] enum:
-//!
-//! * [`Source::Http`]: Download straight from an URL. It is expected that the URL points to a zip
-//! archive which contains the mod, either directly or in a subfolder.
-//! * [`Source::ContentDb`]: Refers to a package on the ContentDB. The [`Downloader`] will consult
-//! the API to get the right download URL.
-//! * [`Source::ModId`]: Refers to a simple mod name. Note that this specification can be
-//! ambiguous, in which case the [`Downloader`] will return an error.
-//!
-//! The actual download work is done by a [`Downloader`]. Each [`Downloader`] has its own temporary
-//! directory, in which any mods are downloaded and extracted. If you drop the [`Downloader`],
-//! those mods will be deleted and the objects pointing to the now-gone directories are no longer
-//! useful.
-
-use std::{
- fs,
- io::{Cursor, Read},
- str::FromStr,
-};
-
-use tempdir::TempDir;
-use url::Url;
-use uuid::Uuid;
-use zip::ZipArchive;
-
-use super::{
- contentdb::{ContentDb, ContentId},
- error::{Error, Result},
- minemod::{self, ModContainer, ModId},
-};
-
-/// A source determines where a mod should be loaded from.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum Source {
- /// Download a mod archive through HTTP.
- Http(Url),
- /// Download a mod from the Content DB, using the given user- and package name.
- ContentDb(ContentId),
- /// Search the Content DB for a given mod ID.
- ///
- /// The download may fail if there are multiple mods providing the same ID.
- ModId(ModId),
-}
-
-impl FromStr for Source {
- type Err = Error;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- if s.starts_with("http://") || s.starts_with("https://") {
- let url = Url::parse(s)?;
- return Ok(Source::Http(url));
- }
- let groups = regex!("^([^/]+)/([^/]+)$").captures(s);
- if let Some(groups) = groups {
- return Ok(Source::ContentDb((
- groups.get(1).unwrap().as_str().into(),
- groups.get(2).unwrap().as_str().into(),
- )));
- }
-
- if !s.contains(' ') {
- return Ok(Source::ModId(s.into()));
- }
-
- Err(Error::InvalidSourceSpec(s.into()))
- }
-}
-
-/// A downloader is responsible for downloading mods from various sources.
-///
-/// Note that the objects that the [`Downloader`] creates will not work after the downloader has
-/// been destroyed, as the temporary files will be lost.
-#[derive(Debug)]
-pub struct Downloader {
- temp_dir: TempDir,
- content_db: ContentDb,
-}
-
-impl Downloader {
- /// Create a new [`Downloader`], refering to the default ContentDB.
- pub fn new() -> Result<Downloader> {
- Downloader::with_content_db(Default::default())
- }
-
- /// Create a new [`Downloader`] that points to a specific ContentDB instance.
- pub fn with_content_db(content_db: ContentDb) -> Result<Downloader> {
- let temp_dir = TempDir::new(env!("CARGO_PKG_NAME"))?;
- Ok(Downloader {
- temp_dir,
- content_db,
- })
- }
-
- /// Download a mod from the given source.
- ///
- /// This function may download either a mod ([`minemod::MineMod`]) or a modpack
- /// ([`minemod::Modpack`]), therefore it returns a trait object that can be queried for the
- /// required information.
- ///
- /// Note that the object will be useless when the [`Downloader`] is dropped, as the temporary
- /// directory containing the downloaded data will be lost. Use [`ModContainer::install_to`] to
- /// copy the mod content to a different directory.
- pub fn download(&self, source: &Source) -> Result<Box<dyn ModContainer>> {
- match *source {
- Source::Http(ref url) => self.download_http(url),
- Source::ContentDb((ref user, ref package)) => {
- let url = self.content_db.download_url(user, package)?;
- self.download_http(&url)
- }
- Source::ModId(ref id) => {
- let candidates = self.content_db.resolve(id)?;
- if candidates.len() != 1 {
- return Err(Error::AmbiguousModId(id.into()));
- }
- self.download_http(&candidates[0].url)
- }
- }
- }
-
- /// Downloads a mod given a HTTP link.
- ///
- /// The [`Downloader`] expects to receive a zipfile containing the mod directory on this link.
- ///
- /// Refer to the module level documentation and [`Downloader::download`] for more information.
- pub fn download_http(&self, url: &Url) -> Result<Box<dyn ModContainer>> {
- let mut reader = ureq::request_url("GET", url).call()?.into_reader();
- let mut data = Vec::new();
- reader.read_to_end(&mut data)?;
- let data = Cursor::new(data);
- let mut archive = ZipArchive::new(data)?;
-
- let dir = self
- .temp_dir
- .path()
- .join(&Uuid::new_v4().to_hyphenated().to_string());
- fs::create_dir(&dir)?;
-
- archive.extract(&dir)?;
-
- // Some archives contain the mod files directly, so try to open it:
- if let Ok(pack) = minemod::open_mod_or_pack(&dir) {
- return Ok(pack);
- }
-
- // If the archive does not contain the mod directly, we instead try the subdirectories that
- // we've extracted.
- for entry in fs::read_dir(&dir)? {
- let entry = entry?;
- let metadata = fs::metadata(&entry.path())?;
- if metadata.is_dir() {
- if let Ok(pack) = minemod::open_mod_or_pack(&entry.path()) {
- return Ok(pack);
- }
- }
- }
- Err(Error::InvalidModDir(dir))
- }
-}