aboutsummaryrefslogtreecommitdiff
path: root/src/contentdb.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/contentdb.rs')
-rw-r--r--src/contentdb.rs127
1 files changed, 0 insertions, 127 deletions
diff --git a/src/contentdb.rs b/src/contentdb.rs
deleted file mode 100644
index d9c4688..0000000
--- a/src/contentdb.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-//! Module to interact with the Minetest Content DB website.
-
-use once_cell::sync::Lazy;
-use scraper::{Html, Selector};
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-use super::error::{Error, Result};
-
-/// The identification of content on Content DB. Consists of the username and the package name.
-pub type ContentId = (String, String);
-
-/// The URL of the default Content DB website to use.
-pub static DEFAULT_INSTANCE: Lazy<Url> =
- Lazy::new(|| Url::parse("https://content.minetest.net/").expect("Invalid default URL"));
-
-/// The metapackage selector to scrape the packages.
-static PROVIDES_SELECTOR: Lazy<Selector> =
- Lazy::new(|| Selector::parse("ul.d-flex").expect("Invalid selector"));
-
-static A_SELECTOR: Lazy<Selector> = Lazy::new(|| Selector::parse("a").expect("Invalid selector"));
-
-/// (Partial) metadata of a content item, as returned by the Content DB API.
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
-pub struct ContentMeta {
- /// Username of the author.
- pub author: String,
- /// Name of the package.
- pub name: String,
- /// A list of mods that are provided by this package.
- pub provides: Vec<String>,
- /// The short description of the package.
- pub short_description: String,
- /// The (human-readable) title of this package.
- pub title: String,
- /// The type of the package ("mod", "game", "txp")
- #[serde(rename = "type")]
- pub typ: String,
- /// The download URL of the package.
- pub url: Url,
-}
-
-/// The main access point for Content DB queries.
-#[derive(Debug, Clone)]
-pub struct ContentDb {
- base_url: Url,
-}
-
-impl Default for ContentDb {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl ContentDb {
- /// Create a new Content DB accessor pointing to the default instance.
- pub fn new() -> ContentDb {
- ContentDb {
- base_url: DEFAULT_INSTANCE.clone(),
- }
- }
-
- /// Find suitable candidates that provide the given modname.
- pub fn resolve(&self, modname: &str) -> Result<Vec<ContentMeta>> {
- let path = format!("metapackages/{}", modname);
- let endpoint = self
- .base_url
- .join(&path)
- .map_err(|_| Error::InvalidModId(modname.into()))?;
-
- let body = ureq::request_url("GET", &endpoint).call()?.into_string()?;
-
- let dom = Html::parse_document(&body);
- let provides = dom
- .select(&PROVIDES_SELECTOR)
- .next()
- .ok_or(Error::InvalidScrape)?;
-
- let candidates: Vec<ContentId> = provides
- .select(&A_SELECTOR)
- .filter_map(|a| a.value().attr("href"))
- .filter_map(extract_content_id)
- .collect();
-
- let mut good_ones = Vec::new();
-
- for (user, package) in candidates {
- let path = format!("api/packages/{}/{}/", user, package);
- let endpoint = self
- .base_url
- .join(&path)
- .expect("The parsed path was wrong");
- let response: ContentMeta = ureq::request_url("GET", &endpoint).call()?.into_json()?;
-
- // While resolving, we only care about actual mods that we can install. If a game
- // provides a certain metapackage, it is pretty much useless for us (and often just
- // there because a mod in that game provides the metapackage).
- if response.typ == "mod" {
- good_ones.push(response)
- }
- }
-
- Ok(good_ones)
- }
-
- /// Retrieve the download url for a given package.
- pub fn download_url(&self, user: &str, package: &str) -> Result<Url> {
- let path = format!("api/packages/{}/{}/", user, package);
- let endpoint = self
- .base_url
- .join(&path)
- .expect("The parsed path was wrong");
- let response: ContentMeta = ureq::request_url("GET", &endpoint).call()?.into_json()?;
- Ok(response.url)
- }
-}
-
-fn extract_content_id(path: &str) -> Option<ContentId> {
- regex!("/packages/([^/]+)/([^/]+)/$")
- .captures(path)
- .map(|c| {
- (
- c.get(1).unwrap().as_str().into(),
- c.get(2).unwrap().as_str().into(),
- )
- })
-}