fml
This commit is contained in:
parent
9c5ece87e7
commit
5344fde4a5
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "umm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde_json = { version = "1.0.114", features = ["preserve_order"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
getset = "0.1.2"
|
||||
futures = "0.3.30"
|
||||
reqwest = { version = "0.11.26", features = ["blocking"] }
|
||||
clap = { version = "4.5.2", features = ["cargo"] }
|
||||
tokio = { version = "1.36.0", features = ["rt-multi-thread", "rt", "macros", "time"] }
|
||||
toml = { version = "0.8.11", features = ["parse"] }
|
||||
hashlink = { version = "0.9.0", features = ["serde", "serde_impl"] }
|
||||
colored = "2.1.0"
|
||||
env_logger = "0.11.3"
|
||||
once_cell = "1.19.0"
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
|
||||
[profile.dev]
|
||||
debug = 1
|
343
src/curseforge.rs
Normal file
343
src/curseforge.rs
Normal file
@ -0,0 +1,343 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::{crate_name, crate_version};
|
||||
use colored::Colorize;
|
||||
use futures::{stream, StreamExt};
|
||||
use futures::executor::block_on;
|
||||
use getset::{CopyGetters, Getters};
|
||||
use hashlink::LinkedHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::Client;
|
||||
use reqwest::header::{HeaderMap, HeaderValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::modpack::{Mod, ModLoader, ModLoaderType, Modpack, ModpackInfo, Relation, RelationType};
|
||||
|
||||
const MANIFEST_TYPE: &str = "minecraftModpack";
|
||||
const MANIFEST_VERSION: usize = 1;
|
||||
const MINECRAFT_GAME_ID: usize = 432;
|
||||
const CURSEFORGE_API_URL: &str = "https://api.curseforge.com";
|
||||
// const CURSEFORGE_API_URL: &str = "http://localhost:6666";
|
||||
const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const CONCURRENT_REQUESTS: usize = 1;
|
||||
static semaphore: Lazy<Semaphore> = Lazy::new(|| Semaphore::new(4));
|
||||
|
||||
static CLIENT: Lazy<Client> = Lazy::new(CurseForgePack::new_client);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct CurseForgePack
|
||||
{
|
||||
author: String,
|
||||
files: Vec<File>,
|
||||
#[allow(non_snake_case)]
|
||||
manifestType: String,
|
||||
#[allow(non_snake_case)]
|
||||
manifestVersion: usize,
|
||||
minecraft: Minecraft,
|
||||
name: String,
|
||||
overrides: String,
|
||||
version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct File
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
fileID: usize,
|
||||
#[allow(non_snake_case)]
|
||||
projectID: usize,
|
||||
required: bool,
|
||||
}
|
||||
|
||||
impl From<&File> for Mod
|
||||
{
|
||||
fn from(value: &File) -> Self {
|
||||
block_on(CurseForgePack::lookup_file(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct Minecraft
|
||||
{
|
||||
#[allow(non_snake_case)]
|
||||
modLoaders: Vec<CFModLoader>,
|
||||
version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(CopyGetters, Getters)]
|
||||
pub struct CFModLoader
|
||||
{
|
||||
#[getset(get = "pub")]
|
||||
id: String,
|
||||
#[getset(get_copy = "pub")]
|
||||
primary: bool,
|
||||
}
|
||||
|
||||
impl From<&ModLoader> for CFModLoader
|
||||
{
|
||||
fn from(value: &ModLoader) -> Self {
|
||||
Self
|
||||
{
|
||||
id: format!("{}-{}", value.loader_type().to_string().to_lowercase(), value.version()),
|
||||
primary: value.primary(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CFModLoader> for ModLoader
|
||||
{
|
||||
fn from(value: &CFModLoader) -> Self {
|
||||
let mut id_iter = value.id.split('-');
|
||||
Self::new(ModLoaderType::from(&CFModLoaderType::try_from(id_iter.next().unwrap()).unwrap()), id_iter.next().unwrap(), value.primary)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub enum CFModLoaderType
|
||||
{
|
||||
Any = 0,
|
||||
Forge = 1,
|
||||
Cauldron = 2,
|
||||
LiteLoader = 3,
|
||||
Fabric = 4,
|
||||
Quilt = 5,
|
||||
NeoForge = 6,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for CFModLoaderType
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
return match value.to_lowercase().as_str()
|
||||
{
|
||||
"any" => Ok(CFModLoaderType::Any),
|
||||
"forge" => Ok(CFModLoaderType::Forge),
|
||||
"cauldron" => Ok(CFModLoaderType::Cauldron),
|
||||
"liteloader" => Ok(CFModLoaderType::LiteLoader),
|
||||
"fabric" => Ok(CFModLoaderType::Fabric),
|
||||
"quilt" => Ok(CFModLoaderType::Quilt),
|
||||
"neoforge" => Ok(CFModLoaderType::NeoForge),
|
||||
_ => Err(()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ModLoaderType> for CFModLoaderType
|
||||
{
|
||||
fn from(value: &ModLoaderType) -> Self {
|
||||
match value
|
||||
{
|
||||
ModLoaderType::Any => CFModLoaderType::Any,
|
||||
ModLoaderType::Forge => CFModLoaderType::Forge,
|
||||
ModLoaderType::Cauldron => CFModLoaderType::Cauldron,
|
||||
ModLoaderType::LiteLoader => CFModLoaderType::LiteLoader,
|
||||
ModLoaderType::Fabric => CFModLoaderType::Fabric,
|
||||
ModLoaderType::Quilt => CFModLoaderType::Quilt,
|
||||
ModLoaderType::NeoForge => CFModLoaderType::NeoForge,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<&CFModLoaderType> for ModLoaderType
|
||||
{
|
||||
fn from(value: &CFModLoaderType) -> ModLoaderType {
|
||||
match value
|
||||
{
|
||||
CFModLoaderType::Any => ModLoaderType::Any,
|
||||
CFModLoaderType::Forge => ModLoaderType::Forge,
|
||||
CFModLoaderType::Cauldron => ModLoaderType::Cauldron,
|
||||
CFModLoaderType::LiteLoader => ModLoaderType::LiteLoader,
|
||||
CFModLoaderType::Fabric => ModLoaderType::Fabric,
|
||||
CFModLoaderType::Quilt => ModLoaderType::Quilt,
|
||||
CFModLoaderType::NeoForge => ModLoaderType::NeoForge,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl From<&Modpack> for CurseForgePack
|
||||
{
|
||||
fn from(modpack: &Modpack) -> Self {
|
||||
let mc = Minecraft {
|
||||
modLoaders: modpack.loader().values().map(CFModLoader::from).collect(),
|
||||
version: modpack.info().minecraft_version().clone(),
|
||||
};
|
||||
|
||||
Self {
|
||||
author: modpack.info().author().clone(),
|
||||
files: block_on(Self::lookup_all_mods(modpack)),
|
||||
manifestType: MANIFEST_TYPE.to_string(),
|
||||
manifestVersion: MANIFEST_VERSION,
|
||||
minecraft: mc,
|
||||
name: modpack.info().title().clone(),
|
||||
overrides: String::from("overrides"),
|
||||
version: modpack.info().modpack_version().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CurseForgePack> for Modpack
|
||||
{
|
||||
fn from(value: &CurseForgePack) -> Self {
|
||||
let i: AtomicUsize = AtomicUsize::new(0);
|
||||
let info = ModpackInfo::new(value.name(), value.author(), value.version(), value.minecraft.version(), "");
|
||||
let loader = value.minecraft().modLoaders().iter().map(|value|
|
||||
(
|
||||
value.id.split('-').next().unwrap().to_owned(),
|
||||
ModLoader::from(value)
|
||||
)
|
||||
).collect::<LinkedHashMap<String, ModLoader>>();
|
||||
|
||||
// let pack = block_on(
|
||||
// async
|
||||
// {
|
||||
// stream::iter(value.files().iter()).map(|value| async { let modification = CurseForgePack::lookup_file(value).await;
|
||||
// println!("i: {}", i.fetch_add(1, Ordering::Relaxed));
|
||||
// (modification.name().clone(), modification)
|
||||
// }).collect::<FuturesUnordered<_>>().await.collect::<LinkedHashMap<_,_>>().await
|
||||
// });
|
||||
|
||||
let pack = block_on(
|
||||
async
|
||||
{
|
||||
stream::iter(value.files().iter()).map(|value| async {
|
||||
let modification = CurseForgePack::lookup_file(value).await;
|
||||
println!("i: {}", i.fetch_add(1, Ordering::Relaxed));
|
||||
(modification.name().clone(), modification)
|
||||
}).buffer_unordered(CONCURRENT_REQUESTS).collect().await
|
||||
|
||||
});
|
||||
Self::new(info, loader, pack)
|
||||
}
|
||||
}
|
||||
|
||||
impl CurseForgePack
|
||||
{
|
||||
async fn lookup_all_mods(modpack: &Modpack) -> Vec<File>
|
||||
{
|
||||
stream::iter(modpack.pack()).map(|(_, modification)| Self::lookup_mod(modification, modpack)).buffer_unordered(CONCURRENT_REQUESTS).collect().await
|
||||
}
|
||||
async fn lookup_mod(modification: &Mod, modpack: &Modpack) -> File
|
||||
{
|
||||
println!("Looking up: {}", modification.name().bright_yellow());
|
||||
let modloader = modpack.primary_loader().map_or_else(|| CFModLoaderType::Any, |value| CFModLoaderType::from(value.loader_type()));
|
||||
|
||||
let project_txt = Self::client().await.get(format!("{CURSEFORGE_API_URL}/v1/mods/search?gameId={MINECRAFT_GAME_ID}&classId=6&slug={}", modification.name())).send().await.unwrap().text().await.unwrap();
|
||||
let project_json = serde_json::from_str::<Value>(&project_txt).unwrap();
|
||||
|
||||
let project_id = project_json["data"][0]["id"].as_u64().expect("Project ID is not an integer number!") as usize;
|
||||
|
||||
let files_txt = Self::client().await.get(format!("{CURSEFORGE_API_URL}/v1/mods/{project_id}/files?gameVersion={}&modLoaderType={}", modpack.info().minecraft_version(), modloader as u8)).send().await.unwrap().text().await.unwrap();
|
||||
let files_json = serde_json::from_str::<Value>(&files_txt).unwrap();
|
||||
|
||||
|
||||
let file_id = match modification.version().as_str()
|
||||
{
|
||||
"*" => files_json["data"].get(0),
|
||||
"/" => {
|
||||
println!("Warning: The following mod has a placeholder version specifier: {}", modification.name().bright_yellow());
|
||||
files_json["data"].get(0)
|
||||
}
|
||||
_ => files_json["data"].as_array().unwrap().iter().find(| x|
|
||||
(*x)["displayName"].as_str().unwrap().contains(modification.version()))
|
||||
};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
let fileID = match file_id
|
||||
{
|
||||
Some(v) => v["id"].as_u64().unwrap() as usize,
|
||||
None => panic!("Could not lookup file for {modification:?}"),
|
||||
};
|
||||
|
||||
println!("Looked up mod: {}", modification.name().green());
|
||||
|
||||
File
|
||||
{
|
||||
fileID,
|
||||
projectID: project_id,
|
||||
required: !*modification.optional(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn lookup_file(file: &File) -> Mod
|
||||
{
|
||||
let project_txt = Self::client().await.get(format!("{CURSEFORGE_API_URL}/v1/mods/{}", file.projectID)).send().await.unwrap().text().await.unwrap();
|
||||
let project_json = serde_json::from_str::<Value>(&project_txt).unwrap();
|
||||
|
||||
println!("ProjectID: {}", file.projectID);
|
||||
println!("FileID: {}", file.fileID);
|
||||
let file_txt = Self::client().await.get(format!("{CURSEFORGE_API_URL}/v1/mods/{}/files/{}", file.projectID, file.fileID)).send().await.unwrap();
|
||||
println!("FileToJSON");
|
||||
let file_json = serde_json::from_str::<Value>(&file_txt.text().await.unwrap()).unwrap();
|
||||
|
||||
println!("Relations");
|
||||
let relations = match file_json["dependencies"].as_array()
|
||||
{
|
||||
Some(a) => { stream::iter(a.iter())
|
||||
.map(|dep| async {
|
||||
let mod_txt = Self::client().await.get(format!("{CURSEFORGE_API_URL}/v1/mods/{}", dep["modId"])).send().await.unwrap().text().await.unwrap();
|
||||
let mod_json: Value = serde_json::from_str(mod_txt.as_str()).unwrap();
|
||||
Relation::new(mod_json["data"]["slug"].as_str().unwrap().to_owned(), RelationType::try_from(dep["relationType"].as_u64().unwrap() as usize).unwrap())
|
||||
}
|
||||
).buffer_unordered(CONCURRENT_REQUESTS).collect().await},
|
||||
None => Vec::new(),
|
||||
};
|
||||
|
||||
|
||||
println!("Mod");
|
||||
let modification = Mod::new(
|
||||
project_json["data"]["name"].as_str().unwrap().to_string(),
|
||||
"/".to_string(), // CurseForge does not store mod version number...
|
||||
!file.required,
|
||||
None,
|
||||
relations,
|
||||
);
|
||||
println!("Looked up: {}", modification.name().green());
|
||||
// sleep(Duration::from_millis(250)).await;
|
||||
modification
|
||||
}
|
||||
|
||||
async fn client() -> Client
|
||||
{
|
||||
let permit = semaphore.acquire().await.unwrap();
|
||||
CLIENT.clone()
|
||||
}
|
||||
|
||||
fn new_client() -> Client
|
||||
{
|
||||
let mut headers = HeaderMap::new();
|
||||
let api_key = env::var("CURSEFORGE_API_KEY").expect("You must supply a Curseforge API key trough setting the CURSEFORGE_API_KEY environment variable!");
|
||||
headers.insert("x-api-key", HeaderValue::from_str(api_key.as_str()).unwrap());
|
||||
|
||||
Client::builder()
|
||||
.default_headers(headers)
|
||||
.user_agent(format!("{}/{}",crate_name!(),crate_version!()))
|
||||
.timeout(HTTP_TIMEOUT)
|
||||
.connect_timeout(HTTP_TIMEOUT)
|
||||
.http2_keep_alive_timeout(HTTP_TIMEOUT)
|
||||
.pool_idle_timeout(HTTP_TIMEOUT)
|
||||
.build().expect("Could not build Curseforge Client!")
|
||||
}
|
||||
}
|
30
src/main.rs
Normal file
30
src/main.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::fs::{File, read_to_string};
|
||||
use crate::curseforge::CurseForgePack;
|
||||
use crate::modpack::Modpack;
|
||||
use std::env;
|
||||
|
||||
pub mod modpack;
|
||||
pub mod curseforge;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env::set_var("RUST_LOG", "debug");
|
||||
env_logger::init();
|
||||
|
||||
// let pack_str = read_to_string("pack.toml").expect("Could not read pack.toml to String");
|
||||
// let pack: Modpack = toml::from_str(&pack_str).expect("Could not parse pack.toml");
|
||||
// let cf: CurseForgePack = (&pack).into();
|
||||
// let manifest = File::create("manifest.json").unwrap();
|
||||
// serde_json::to_writer(manifest, &cf).unwrap()
|
||||
|
||||
let all_manifest = read_to_string("mods/manifest.json").unwrap();
|
||||
let cfp: CurseForgePack = serde_json::from_str(&all_manifest).unwrap();
|
||||
println!("Create UMM");
|
||||
let umm: Modpack = Modpack::from(&cfp);
|
||||
let mod_pack= File::create("mods/pack.toml").unwrap();
|
||||
serde_json::to_writer(mod_pack, &umm).unwrap();
|
||||
println!("Convert to CFP");
|
||||
let cfp2 = CurseForgePack::from(&umm);
|
||||
let cfp2_file = File::create("manifest_2.json").unwrap();
|
||||
serde_json::to_writer(cfp2_file, &cfp2).unwrap();
|
||||
}
|
231
src/modpack.rs
Normal file
231
src/modpack.rs
Normal file
@ -0,0 +1,231 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::path::PathBuf;
|
||||
use getset::{CopyGetters, Getters};
|
||||
use hashlink::LinkedHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct Modpack
|
||||
{
|
||||
info: ModpackInfo,
|
||||
loader: LinkedHashMap<String, ModLoader>,
|
||||
pack: LinkedHashMap<String, Mod>,
|
||||
}
|
||||
|
||||
impl Modpack
|
||||
{
|
||||
pub fn new(info: ModpackInfo, loader: LinkedHashMap<String, ModLoader>, pack: LinkedHashMap<String, Mod>) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
info,
|
||||
loader,
|
||||
pack,
|
||||
}
|
||||
}
|
||||
pub fn primary_loader(&self) -> Option<&ModLoader>
|
||||
{
|
||||
self.loader().values().find(|loader|
|
||||
{
|
||||
loader.primary()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct ModpackInfo
|
||||
{
|
||||
title: String,
|
||||
author: String,
|
||||
modpack_version: String,
|
||||
minecraft_version: String,
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl ModpackInfo
|
||||
{
|
||||
pub fn new(
|
||||
title: impl Into<String>,
|
||||
author: impl Into<String>,
|
||||
modpack_version: impl Into<String>,
|
||||
minecraft_version: impl Into<String>,
|
||||
descriptipn: impl Into<String>,
|
||||
) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
title: title.into(),
|
||||
author: author.into(),
|
||||
modpack_version: modpack_version.into(),
|
||||
minecraft_version: minecraft_version.into(),
|
||||
description: descriptipn.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(CopyGetters, Getters)]
|
||||
pub struct ModLoader
|
||||
{
|
||||
#[getset(get = "pub")]
|
||||
loader_type: ModLoaderType,
|
||||
#[getset(get = "pub")]
|
||||
version: String,
|
||||
#[getset(get_copy = "pub")]
|
||||
primary: bool,
|
||||
}
|
||||
|
||||
impl ModLoader
|
||||
{
|
||||
pub fn new(loader_type: ModLoaderType, version: impl Into<String>, primary: bool) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
loader_type,
|
||||
version: version.into(),
|
||||
primary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub enum ModLoaderType
|
||||
{
|
||||
Any = 0,
|
||||
Forge = 1,
|
||||
Cauldron = 2,
|
||||
LiteLoader = 3,
|
||||
Fabric = 4,
|
||||
Quilt = 5,
|
||||
NeoForge = 6,
|
||||
}
|
||||
|
||||
impl Display for ModLoaderType
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self
|
||||
{
|
||||
Self::Any => write!(f, "Any"),
|
||||
Self::Forge => write!(f, "Forge"),
|
||||
Self::Cauldron => write!(f, "Cauldron"),
|
||||
Self::LiteLoader => write!(f, "LiteLoader"),
|
||||
Self::Fabric => write!(f, "Fabric"),
|
||||
Self::Quilt => write!(f, "Quilt"),
|
||||
Self::NeoForge => write!(f, "NeoForge"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct Mod
|
||||
{
|
||||
name: String,
|
||||
version: String,
|
||||
optional: bool,
|
||||
overrule: Option<PathBuf>,
|
||||
relations: Vec<Relation>,
|
||||
}
|
||||
|
||||
impl Mod
|
||||
{
|
||||
pub fn new(
|
||||
name: impl Into<String>,
|
||||
version: impl Into<String>,
|
||||
optional: bool,
|
||||
overrule: Option<PathBuf>,
|
||||
relations: Vec<Relation>,
|
||||
) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
name: name.into(),
|
||||
version: version.into(),
|
||||
optional,
|
||||
overrule,
|
||||
relations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
#[derive(Getters)]
|
||||
#[getset(get = "pub")]
|
||||
pub struct Relation
|
||||
{
|
||||
mod_slug: String,
|
||||
relation_type: RelationType,
|
||||
}
|
||||
|
||||
impl Relation
|
||||
{
|
||||
pub fn new(mod_slug: impl Into<String>, relation_type: RelationType) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
mod_slug: mod_slug.into(),
|
||||
relation_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub enum RelationType
|
||||
{
|
||||
EmbeddedLibrary = 1,
|
||||
OptionalDependency = 2,
|
||||
RequiredDependency = 3,
|
||||
Tool = 4,
|
||||
Incompatible = 5,
|
||||
Include = 6,
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for RelationType
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
match value
|
||||
{
|
||||
1 => Ok(Self::EmbeddedLibrary),
|
||||
2 => Ok(Self::OptionalDependency),
|
||||
3 => Ok(Self::RequiredDependency),
|
||||
4 => Ok(Self::Tool),
|
||||
5 => Ok(Self::Incompatible),
|
||||
6 => Ok(Self::Include),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for RelationType
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
match value.to_lowercase().as_str()
|
||||
{
|
||||
"embeddedlibrary" => Ok(Self::EmbeddedLibrary),
|
||||
"optionaldependency" => Ok(Self::OptionalDependency),
|
||||
"requireddependency" => Ok(Self::RequiredDependency),
|
||||
"tool" => Ok(Self::Tool),
|
||||
"incompatible" => Ok(Self::Incompatible),
|
||||
"include" => Ok(Self::Include),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user