diff --git a/src/api/app/actions/build.rs b/src/api/app/actions/build/mod.rs similarity index 100% rename from src/api/app/actions/build.rs rename to src/api/app/actions/build/mod.rs diff --git a/src/api/app/mod.rs b/src/api/app/mod.rs index d965e75..b938c29 100644 --- a/src/api/app/mod.rs +++ b/src/api/app/mod.rs @@ -1,6 +1,8 @@ use std::sync::Arc; use anyhow::{Context, Result}; +use reqwest::Url; +use serde::de::DeserializeOwned; use tokio::sync::RwLock; use super::models::{network::Network, Addon, server::Server}; @@ -16,10 +18,10 @@ pub const APP_USER_AGENT: &str = concat!( ); pub struct App { - http_client: reqwest::Client, - server: Option>>, - network: Option>>, - ci: bool, + pub http_client: reqwest::Client, + pub server: Option>>, + pub network: Option>>, + pub ci: bool, } impl App { @@ -37,6 +39,17 @@ impl App { }) } + pub async fn http_get_json(&self, url: impl Into) -> Result { + Ok(self.http_client + .get(url.into()) + .send() + .await? + .error_for_status()? + .json() + .await? + ) + } + pub async fn collect_addons(&self) -> Result> { let mut addons = vec![]; diff --git a/src/api/mod.rs b/src/api/mod.rs index 1fa4cff..7b76171 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -2,3 +2,4 @@ pub mod models; pub mod app; pub mod tools; pub mod utils; +pub mod sources; diff --git a/src/api/models/addon/mod.rs b/src/api/models/addon/mod.rs index d33db7d..0cd1e44 100644 --- a/src/api/models/addon/mod.rs +++ b/src/api/models/addon/mod.rs @@ -1,7 +1,5 @@ mod addon; -mod addon_source; mod addon_type; pub use addon::*; -pub use addon_source::*; pub use addon_type::*; diff --git a/src/api/models/lockfile/mod.rs b/src/api/models/lockfile/mod.rs new file mode 100644 index 0000000..0b3fdb8 --- /dev/null +++ b/src/api/models/lockfile/mod.rs @@ -0,0 +1,3 @@ +pub struct Lockfile { + +} diff --git a/src/api/models/mod.rs b/src/api/models/mod.rs index 2be555e..6497d3e 100644 --- a/src/api/models/mod.rs +++ b/src/api/models/mod.rs @@ -1,6 +1,7 @@ mod modpack_source; mod step; mod env; +mod source; pub mod addon; pub mod packwiz; @@ -8,8 +9,10 @@ pub mod mrpack; pub mod unsup; pub mod network; pub mod server; +pub mod lockfile; pub use modpack_source::*; pub use step::*; pub use addon::*; pub use env::*; +pub use source::*; diff --git a/src/api/models/mrpack/mod.rs b/src/api/models/mrpack/mod.rs index e69de29..482e5c3 100644 --- a/src/api/models/mrpack/mod.rs +++ b/src/api/models/mrpack/mod.rs @@ -0,0 +1,27 @@ +pub const MRPACK_INDEX_FILE: &str = "modrinth.index.json"; + +mod mrpack_index; +mod mrpack_file; + +use anyhow::Result; +pub use mrpack_index::*; +pub use mrpack_file::*; + +use crate::api::{app::App, utils::accessor::Accessor}; + +use super::Addon; + +pub async fn resolve_mrpack_addons( + app: &App, + accessor: Accessor, +) -> Result> { + + + todo!() +} + +impl MRPackFile { + pub async fn into_addon(&self) -> Result { + todo!() + } +} diff --git a/src/api/models/mrpack/mrpack_file.rs b/src/api/models/mrpack/mrpack_file.rs new file mode 100644 index 0000000..3aaaeed --- /dev/null +++ b/src/api/models/mrpack/mrpack_file.rs @@ -0,0 +1,27 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct MRPackFile { + path: String, + hashes: HashMap, + env: Option, + file_size: u64, + downloads: Vec, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Env { + pub client: EnvSupport, + pub server: EnvSupport, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] +#[serde(rename_all = "snake_case")] +pub enum EnvSupport { + Required, + Optional, + Unsupported, +} diff --git a/src/api/models/mrpack/mrpack_index.rs b/src/api/models/mrpack/mrpack_index.rs new file mode 100644 index 0000000..9611e3f --- /dev/null +++ b/src/api/models/mrpack/mrpack_index.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use super::MRPackFile; + +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct MRPackIndex { + pub game: String, + pub name: String, + pub version_id: String, + pub summary: Option, + pub files: Vec, + pub dependencies: HashMap, +} diff --git a/src/api/models/server/mod.rs b/src/api/models/server/mod.rs index cd6091b..400f7dd 100644 --- a/src/api/models/server/mod.rs +++ b/src/api/models/server/mod.rs @@ -1,6 +1,6 @@ use serde::{Serialize, Deserialize}; -use super::AddonSource; +use super::Source; mod server_type; mod server_flavor; @@ -14,7 +14,7 @@ pub struct Server { pub name: String, pub port: Option, - pub sources: Vec, + pub sources: Vec, } impl Default for Server { diff --git a/src/api/models/addon/addon_source.rs b/src/api/models/source.rs similarity index 70% rename from src/api/models/addon/addon_source.rs rename to src/api/models/source.rs index 3ad4658..df122d3 100644 --- a/src/api/models/addon/addon_source.rs +++ b/src/api/models/source.rs @@ -5,7 +5,8 @@ use crate::api::{app::App, models::ModpackSource}; use super::Addon; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum AddonSource { +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Source { File { path: String, }, @@ -15,22 +16,23 @@ pub enum AddonSource { }, Modpack { + #[serde(flatten)] modpack: ModpackSource, }, } -impl AddonSource { +impl Source { pub async fn resolve(&self, app: &App) -> Result> { match self { - AddonSource::File { path } => { + Source::File { path } => { Ok(vec![]) } - AddonSource::Folder { path } => { + Source::Folder { path } => { Ok(vec![]) } - AddonSource::Modpack { modpack } => { + Source::Modpack { modpack } => { Ok(vec![]) } } diff --git a/src/api/models/step.rs b/src/api/models/step.rs index e99edca..bc39740 100644 --- a/src/api/models/step.rs +++ b/src/api/models/step.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; use anyhow::Result; use serde::{Deserialize, Serialize}; @@ -18,20 +18,18 @@ pub enum Step { Execute, } -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum CacheStrategy { File { - namespace: String, + namespace: Cow<'static, str>, path: String, }, Indexed { - index_path: String, + namespace: Cow<'static, str>, + path: Option, key: String, - value: String, }, - #[default] - None, } pub enum StepResult { diff --git a/src/api/sources/mod.rs b/src/api/sources/mod.rs index e69de29..a491ad1 100644 --- a/src/api/sources/mod.rs +++ b/src/api/sources/mod.rs @@ -0,0 +1,33 @@ +use std::collections::HashMap; + +use anyhow::Result; + +use super::{app::App, models::{CacheStrategy, Step}, utils::url::get_filename_from_url}; + +pub mod vanilla; + +pub async fn resolve_steps_for_url( + app: &App, + url: impl Into, + filename: Option, +) -> Result> { + let url: String = url.into(); + + let filename = filename.unwrap_or_else(|| { + get_filename_from_url(&url) + }); + + Ok(vec![ + Step::CacheCheck(CacheStrategy::Indexed { + namespace: "url".into(), + path: None, + key: url.clone(), + }), + Step::Download { + url: url.into(), + filename, + size: None, + hashes: HashMap::new(), + } + ]) +} diff --git a/src/api/sources/vanilla/mod.rs b/src/api/sources/vanilla/mod.rs new file mode 100644 index 0000000..00d45cb --- /dev/null +++ b/src/api/sources/vanilla/mod.rs @@ -0,0 +1,7 @@ +use crate::api::app::App; + +pub struct VanillaAPI<'a>(pub &'a App); + +impl<'a> VanillaAPI<'a> { + +} diff --git a/src/api/utils/accessor.rs b/src/api/utils/accessor.rs index 2315fcb..487d8ff 100644 --- a/src/api/utils/accessor.rs +++ b/src/api/utils/accessor.rs @@ -1,10 +1,36 @@ -use std::path::PathBuf; +use std::{ffi::OsString, fs::DirEntry, io::{Read, Seek}, path::PathBuf}; + +use anyhow::{anyhow, Result}; +use serde::de::DeserializeOwned; +use zip::ZipArchive; + +use crate::api::app::App; + +pub trait ReadSeek: std::io::Read + Seek {} pub enum Accessor { Local(PathBuf), Remote(reqwest::Url), + Zip(ZipArchive>), } impl Accessor { - + pub async fn dir(&self) -> Result> { + match self { + Accessor::Zip(zip) => Ok(zip.file_names().map(ToOwned::to_owned).collect()), + Accessor::Local(path) => Ok(path.read_dir()? + .filter_map(|r| r.ok()) + .map(|n| n.file_name().to_string_lossy().into_owned()) + .collect()), + Accessor::Remote(_) => Err(anyhow!("cannot dir() Accessor::Remote")), + } + } + + pub async fn json(&mut self, app: &App, path: &str) -> Result { + match self { + Accessor::Local(base) => Ok(serde_json::from_reader(std::fs::File::open(base.join(path))?)?), + Accessor::Zip(zip) => Ok(serde_json::from_reader(zip.by_name(path)?)?), + Accessor::Remote(url) => Ok(app.http_get_json(url.join(path)?).await?), + } + } } diff --git a/src/api/utils/mod.rs b/src/api/utils/mod.rs index 13ff7f6..7934fba 100644 --- a/src/api/utils/mod.rs +++ b/src/api/utils/mod.rs @@ -1,3 +1,4 @@ pub mod hashing; pub mod accessor; pub mod serde; +pub mod url; diff --git a/src/api/utils/url.rs b/src/api/utils/url.rs new file mode 100644 index 0000000..e515091 --- /dev/null +++ b/src/api/utils/url.rs @@ -0,0 +1,4 @@ +pub fn get_filename_from_url(url: &str) -> String { + let url_clean = url.split(&['?', '#'][..]).next().unwrap(); + url_clean.split('/').last().unwrap().to_string() +} diff --git a/src/commands/build.rs b/src/commands/build.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 43763f1..d07ba46 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1 +1,2 @@ pub mod init; +pub mod build;