#![allow(rustdoc::bare_urls)]
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use std::fs::read_to_string;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct SearchProjects {
pub hits: Box<[Hit]>,
pub offset: u32,
pub limit: u32,
pub total_hits: u64,
}
impl SearchProjects {
pub fn len(&self) -> usize {
self.hits.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Hit {
pub slug: String,
pub title: String,
pub description: String,
pub categories: Box<String>,
pub client_side: String,
pub server_side: String,
pub project_type: String,
pub downloads: usize,
pub icon_url: Option<String>,
pub color: Option<usize>,
pub project_id: String,
pub author: String,
pub display_categories: Box<[String]>,
pub versions: Box<[String]>,
pub follows: usize,
pub date_created: String,
pub date_modified: String,
pub latest_version: Option<String>,
pub license: String,
pub gallery: Box<[String]>,
}
pub enum Attributes {
Loader,
Name,
VersionType,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct RinthProject {
pub slug: String,
pub title: String,
pub description: String,
pub categories: Box<[String]>,
pub client_side: String,
pub server_side: String,
pub body: String,
pub status: String,
pub requested_status: Option<String>,
pub additional_categories: Box<[String]>,
pub project_type: String,
pub downloads: u32,
pub icon_url: Option<String>,
pub id: String,
pub team: String,
pub body_url: Option<String>,
pub published: String,
pub updated: String,
pub followers: u32,
pub versions: Box<[String]>,
pub game_versions: Box<[String]>,
pub loaders: Box<[String]>,
}
pub type RinthProjects = Box<[RinthProject]>;
pub struct Dependencies {
pub projects: Box<[RinthProject]>,
pub versions: Box<[DependencyInfo]>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct DependencyInfo {
pub name: String,
pub version_number: String,
pub dependencies: Box<[Dependency]>,
pub game_versions: Box<[String]>,
pub version_type: String,
pub loaders: Box<[String]>,
pub featured: bool,
pub status: String,
pub id: String,
pub project_id: String,
pub author_id: String,
pub date_published: String,
pub downloads: u32,
pub changelog: String,
pub changelog_url: Option<String>,
pub files: Box<[DependencyFiles]>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Dependency {
pub version_id: Option<String>,
pub project_id: Option<String>,
pub file_name: Option<String>,
pub dependency_type: String,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DependencyFiles {
pub hashes: std::collections::HashMap<String, String>,
pub url: String,
pub filename: String,
pub primary: bool,
pub size: u32,
pub file_type: Option<String>,
}
pub struct ProjectVersions {
pub versions: Box<[DependencyInfo]>,
}
pub type DependencyInfosH = HashMap<String, DependencyInfo>;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct RinthVersion {
pub name: String,
pub version_number: String,
pub game_versions: Vec<String>,
pub version_type: String,
pub loaders: Vec<String>,
pub featured: bool,
pub id: String,
pub project_id: String,
pub author_id: String,
pub date_published: String,
pub downloads: u64,
pub files: Vec<RinthFile>,
pub dependencies: Vec<Dependency>,
}
impl RinthVersion {
pub fn get_file_url(&self) -> &str {
&self.files[0].url
}
pub fn get_file_name(&self) -> &str {
&self.files[0].filename
}
pub fn get_hashes(&self) -> &Hashes {
&self.files[0].hashes
}
pub fn get_size(&self) -> usize {
self.files[0].size
}
pub fn get_loader(&self) -> &str {
&self.loaders[0]
}
pub fn is_fabric(&self) -> bool {
self.loaders.iter().any(|l| l == "fabric")
}
pub fn has_dependencies(&self) -> bool {
!self.dependencies.is_empty()
}
}
pub type RinthVersions = Vec<RinthVersion>;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct Hashes {
pub sha512: String,
pub sha1: String,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct RinthFile {
pub hashes: Hashes,
pub url: String,
pub filename: String,
pub primary: bool,
pub size: usize,
}
pub type RinthCategories = Vec<Category>;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct Category {
pub icon: String,
pub name: String,
pub project_type: String,
pub header: String,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct RinthModpack {
#[cfg_attr(feature = "serde", serde(rename = "formatVersion"))]
pub format_version: usize,
pub game: String,
#[cfg_attr(feature = "serde", serde(rename = "versionId"))]
pub version_id: String,
pub name: PathBuf,
pub files: Vec<RinthMdFiles>,
}
impl RinthModpack {
pub fn new() -> RinthModpack {
RinthModpack::default()
}
pub fn new_with(version_id: String, name: PathBuf, files: Vec<RinthMdFiles>) -> Self {
Self {
format_version: 1,
game: "minecraft".to_string(),
version_id,
name,
files,
}
}
pub fn get_mods(&self) -> &[RinthMdFiles] {
&self.files
}
pub fn get_mut_mods(&mut self) -> &mut Vec<RinthMdFiles> {
&mut self.files
}
pub fn get_name(&self) -> String {
self.name.display().to_string()
}
pub fn get_files(&self) -> &Vec<RinthMdFiles> {
&self.files
}
pub fn add_mod(&mut self, new_mod: RinthMdFiles) {
self.files.push(new_mod);
}
#[cfg(feature = "serde")]
pub fn write_mod_pack_with_name(&self) -> std::io::Result<()> {
let j = serde_json::to_string_pretty(self)?;
std::fs::write("modrinth.index.json", j)?;
Ok(())
}
}
impl std::default::Default for RinthModpack {
fn default() -> Self {
RinthModpack {
format_version: 1,
game: "minecraft".to_owned(),
version_id: "0.0.0".to_owned(),
name: "example".into(),
files: Vec::new(),
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
#[derive(Debug, Clone)]
pub struct RinthMdFiles {
pub path: PathBuf,
pub hashes: Hashes,
pub downloads: Vec<String>,
pub file_size: usize,
}
impl From<RinthVersion> for RinthMdFiles {
fn from(version: RinthVersion) -> RinthMdFiles {
RinthMdFiles {
path: ("mods/".to_owned() + version.get_file_name()).into(),
hashes: version.get_hashes().clone(),
downloads: vec![version.get_file_url().to_string()],
file_size: version.get_size(),
}
}
}
impl From<RinthVersionFile> for RinthMdFiles {
fn from(version: RinthVersionFile) -> Self {
Self {
path: ("mods/".to_owned() + &version.name).into(),
hashes: version.files[0].hashes.clone(),
downloads: vec![version.files[0].url.to_string()],
file_size: version.files[0].size,
}
}
}
impl RinthMdFiles {
pub fn get_download_link(&self) -> &str {
&self.downloads[0]
}
pub fn get_id(&self) -> Option<&str> {
for download_link in &self.downloads {
if download_link.contains("modrinth") {
return download_link.split("data/").nth(1).map(|f| &f[0..8]);
}
}
None
}
pub fn get_name(&self) -> &str {
self.path.file_name().unwrap().to_str().unwrap_or_default()
}
pub fn get_path(&self) -> &Path {
&self.path
}
pub fn get_sha1(&self) -> &str {
&self.hashes.sha1
}
pub fn get_sha512(&self) -> &str {
&self.hashes.sha512
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct RinthVersionFile {
pub name: String,
pub version_number: String,
pub game_versions: Vec<String>,
pub version_type: String,
pub loaders: Vec<String>,
pub featured: bool,
pub id: String,
pub project_id: String,
pub author_id: String,
pub date_published: String,
pub downloads: u64,
pub files: Vec<RinthFile>,
#[cfg_attr(feature = "serde", serde(default = "Default::default"))]
pub dependency: Vec<Dependency>,
}
#[cfg(feature = "serde")]
pub fn load_rinth_pack<I: AsRef<Path>>(pack_path: I) -> Option<RinthModpack> {
read_to_string(&pack_path)
.map(|s| serde_json::from_str(&s).ok())
.ok()
.flatten()
}