Skip to content

Commit

Permalink
downloading works
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAlan404 committed Jun 17, 2024
1 parent bb6e371 commit 6135531
Show file tree
Hide file tree
Showing 20 changed files with 329 additions and 132 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ cliclack = "0.2.5"
os_info = { version = "3.7", default-features = false }
lazy_static = "1.4"
bytes = "1.6"
murmur2 = "0.1.0"
12 changes: 12 additions & 0 deletions TECHNICAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Technical Documentation

meow

Everything starts at the [`App`](./src/api/app/mod.rs) struct.

- `App::collect_sources(&self) -> Vec<Source>`
- `Source::resolve_addons(&App) -> Vec<Addon>`
- `App::collect_addons(&self) -> Vec<Addon>`
- `Addon::resolve_steps(&App) -> Vec<Step>`
- `App::execute_steps(&self, &[Step]) -> Result<()>`
- `App::execute_step(&self, Step) -> Result<StepResult>`
29 changes: 27 additions & 2 deletions src/api/app/actions/build/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
use crate::api::app::App;
use std::path::Path;

use anyhow::Result;

use crate::api::{app::App, models::Addon};

impl App {

pub async fn action_install_jar(&self) -> Result<()> {

Check warning on line 8 in src/api/app/actions/build/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

method `action_install_jar` is never used

warning: method `action_install_jar` is never used --> src/api/app/actions/build/mod.rs:8:18 | 7 | impl App { | -------- method in this implementation 8 | pub async fn action_install_jar(&self) -> Result<()> { | ^^^^^^^^^^^^^^^^^^


Ok(())
}

Check warning on line 12 in src/api/app/actions/build/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused `async` for function with no await statements

warning: unused `async` for function with no await statements --> src/api/app/actions/build/mod.rs:8:5 | 8 | / pub async fn action_install_jar(&self) -> Result<()> { 9 | | 10 | | 11 | | Ok(()) 12 | | } | |_____^ | = help: consider removing the `async` from this function = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_async

pub async fn action_install_addon(&self, base: &Path, addon: &Addon) -> Result<()> {
let steps = addon.resolve_steps(&self).await?;

Check failure on line 15 in src/api/app/actions/build/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> src/api/app/actions/build/mod.rs:15:41 | 15 | let steps = addon.resolve_steps(&self).await?; | ^^^^^ help: change this to: `self` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: to override `-D clippy::all` add `#[allow(clippy::needless_borrow)]`
let dir = base.join(addon.target.as_str());
self.execute_steps(&dir, &steps).await?;
Ok(())
}

pub async fn action_install_addons(&self, base: &Path) -> Result<()> {
let addons = self.collect_addons().await?;

for addon in &addons {
self.action_install_addon(base, addon).await?;
}

Ok(())
}
}
7 changes: 7 additions & 0 deletions src/api/app/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ use std::{
use anyhow::{Context, Result};
use serde::de::DeserializeOwned;

use crate::api::step::CacheLocation;

pub struct Cache(pub Option<PathBuf>);

impl Cache {
pub fn new(path: Option<PathBuf>) -> Self {
Self(path)
}

pub fn loc(&self, loc: Option<&CacheLocation>) -> Option<PathBuf> {
let loc = loc?;
self.join(&format!("{}/{}", loc.0, loc.1))
}

pub fn join(&self, path: &str) -> Option<PathBuf> {
Some(self.0.as_ref()?.join(path))
}
Expand Down
3 changes: 2 additions & 1 deletion src/api/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use super::{models::{network::{Network, NETWORK_TOML}, server::{Server, SERVER_T
pub mod actions;
pub mod cache;
pub mod options;
pub mod step;

pub const APP_USER_AGENT: &str = concat!(
env!("CARGO_PKG_NAME"),
Expand All @@ -39,7 +40,7 @@ impl App {
.build()
.context("Initializing http_client")?;

let cache = Cache::new(dirs::cache_dir());
let cache = Cache::new(dirs::cache_dir().map(|p| p.join("mcman")));

let (server_path, server) = try_find_toml_upwards::<Server>(SERVER_TOML)?.unzip();
let (network_path, network) = try_find_toml_upwards::<Network>(NETWORK_TOML)?.unzip();
Expand Down
88 changes: 88 additions & 0 deletions src/api/app/step.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::path::Path;

use anyhow::{anyhow, bail, Result};
use tokio_stream::StreamExt;

use crate::api::{models::Addon, step::{CacheLocation, Step, StepResult}, utils::hashing::get_best_hash};

Check warning on line 6 in src/api/app/step.rs

View workflow job for this annotation

GitHub Actions / clippy

unused imports: `CacheLocation`, `models::Addon`

warning: unused imports: `CacheLocation`, `models::Addon` --> src/api/app/step.rs:6:18 | 6 | use crate::api::{models::Addon, step::{CacheLocation, Step, StepResult}, utils::hashing::get_best_hash}; | ^^^^^^^^^^^^^ ^^^^^^^^^^^^^

use super::App;

impl App {
pub async fn execute_steps(&self, dir: &Path, steps: &[Step]) -> Result<()> {
for step in steps {
let res = self.execute_step(dir, step).await?;

if res == StepResult::Skip {
break;
}
}

Ok(())
}

pub async fn execute_step(&self, dir: &Path, step: &Step) -> Result<StepResult> {
match step {
Step::CacheCheck(metadata) => {
if let Some(cache) = &metadata.cache {

Check warning on line 26 in src/api/app/step.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `cache`

warning: unused variable: `cache` --> src/api/app/step.rs:26:29 | 26 | if let Some(cache) = &metadata.cache { | ^^^^^ help: if this is intentional, prefix it with an underscore: `_cache`

}

Ok(StepResult::Continue)
},

Step::Download { url, metadata } => {
let cache_destination = self.cache.loc(metadata.cache.as_ref());
let output_destination = dir.join(&metadata.filename);

let res = self.http_get(url).await?;

let content_length = res.content_length();
match (metadata.size, content_length) {
(Some(a), Some(b)) if a != b => {
bail!("Mismatched Content-Length! Expected {a}, recieved {b}");
},
_ => {},
}

let mut stream = res.bytes_stream();

let mut hasher = get_best_hash(&metadata.hashes).map(|(format, content)| {
(format, format.get_digest(), content)
});

let target_destination = cache_destination.as_ref().unwrap_or(&output_destination);
tokio::fs::create_dir_all(target_destination.parent().ok_or(anyhow!("No parent"))?).await?;

let target_file = tokio::fs::File::create(&target_destination).await?;
let mut target_writer = tokio::io::BufWriter::new(target_file);

while let Some(item) = stream.try_next().await? {
if let Some((_, ref mut digest, _ )) = hasher {
digest.update(&item);
}

tokio::io::copy(&mut item.as_ref(), &mut target_writer).await?;
}

if let Some((_, hasher, content)) = hasher {
let hash = hex::encode(&hasher.finalize());

if hash != content {
bail!("Mismatched hash!");
}
}

if let Some(cache_destination) = cache_destination {
tokio::fs::create_dir_all(output_destination.parent().ok_or(anyhow!("No parent"))?).await?;
tokio::fs::copy(&cache_destination, &output_destination).await?;
}

println!("Downloaded {}", metadata.filename);

Ok(StepResult::Continue)
},

Step::Execute => Ok(StepResult::Continue),
}
}
}
6 changes: 5 additions & 1 deletion src/api/models/addon/addon_target.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -35,4 +35,8 @@ impl AddonTarget {
pub fn from_path(path: &str) -> Self {
Self::from_str(&Path::new(path).parent().map(|p| p.to_string_lossy().into_owned()).unwrap_or(".".to_owned()))

Check warning on line 36 in src/api/models/addon/addon_target.rs

View workflow job for this annotation

GitHub Actions / clippy

called `map(<f>).unwrap_or(<a>)` on an `Option` value

warning: called `map(<f>).unwrap_or(<a>)` on an `Option` value --> src/api/models/addon/addon_target.rs:36:25 | 36 | Self::from_str(&Path::new(path).parent().map(|p| p.to_string_lossy().into_owned()).unwrap_or(".".to_owned())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or = note: `-W clippy::map-unwrap-or` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::map_unwrap_or)]` help: use `map_or(<a>, <f>)` instead | 36 - Self::from_str(&Path::new(path).parent().map(|p| p.to_string_lossy().into_owned()).unwrap_or(".".to_owned())) 36 + Self::from_str(&Path::new(path).parent().map_or(".".to_owned(), |p| p.to_string_lossy().into_owned())) |
}

pub fn to_path(&self) -> PathBuf {

Check warning on line 39 in src/api/models/addon/addon_target.rs

View workflow job for this annotation

GitHub Actions / clippy

method `to_path` is never used

warning: method `to_path` is never used --> src/api/models/addon/addon_target.rs:39:12 | 18 | impl AddonTarget { | ---------------- method in this implementation ... 39 | pub fn to_path(&self) -> PathBuf { | ^^^^^^^ | = note: `#[warn(dead_code)]` on by default
PathBuf::from(self.as_str())
}
}
1 change: 1 addition & 0 deletions src/api/sources/curseforge/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod models;
Empty file.
20 changes: 9 additions & 11 deletions src/api/sources/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::collections::HashMap;

use anyhow::Result;

use super::{app::App, step::{CacheStrategy, Step}, utils::url::get_filename_from_url};
use super::{app::App, step::{FileMeta, Step}, utils::url::get_filename_from_url};

pub mod vanilla;
pub mod modrinth;
pub mod curseforge;

pub async fn resolve_steps_for_url(
app: &App,

Check warning on line 10 in src/api/sources/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `app`

warning: unused variable: `app` --> src/api/sources/mod.rs:10:5 | 10 | app: &App, | ^^^ help: if this is intentional, prefix it with an underscore: `_app`
Expand All @@ -18,17 +17,16 @@ pub async fn resolve_steps_for_url(
get_filename_from_url(&url)
});

let metadata = FileMeta {
cache: None,
filename,
..Default::default()
};

Ok(vec![
Step::CacheCheck(CacheStrategy::Indexed {
namespace: "url".into(),
path: None,
key: url.clone(),
}),
Step::Download {
url: url.into(),

Check failure on line 28 in src/api/sources/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `std::string::String`

error: useless conversion to the same type: `std::string::String` --> src/api/sources/mod.rs:28:18 | 28 | url: url.into(), | ^^^^^^^^^^ help: consider removing `.into()`: `url` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `-D clippy::useless-conversion` implied by `-D clippy::all` = help: to override `-D clippy::all` add `#[allow(clippy::useless_conversion)]`
filename,
size: None,
hashes: HashMap::new(),
metadata,
}
])
}
19 changes: 10 additions & 9 deletions src/api/sources/modrinth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ use anyhow::{anyhow, Result};
use models::{ModrinthFile, ModrinthProject, ModrinthVersion};
use serde::de::DeserializeOwned;

use crate::api::{app::App, step::{CacheStrategy, Step}};
use crate::api::{app::App, step::{CacheLocation, FileMeta, Step}};

mod models;
mod ratelimit;

pub struct ModrinthAPI<'a>(pub &'a App);

Expand Down Expand Up @@ -94,16 +93,18 @@ impl<'a> ModrinthAPI<'a> {
pub async fn resolve_steps(&self, id: &str, version: &str) -> Result<Vec<Step>> {
let (file, version) = self.fetch_file(id, version).await?;

let metadata = FileMeta {
cache: Some(CacheLocation("modrinth".into(), format!("{id}/{}/{}", version.id, file.filename))),
filename: file.filename,
size: Some(file.size),
hashes: file.hashes,
};

Ok(vec![
Step::CacheCheck(CacheStrategy::File {
namespace: "modrinth".into(),
path: format!("{id}/{}/{}", version.id, file.filename),
}),
Step::CacheCheck(metadata.clone()),
Step::Download {
url: file.url,
filename: file.filename,
size: Some(file.size),
hashes: file.hashes,
metadata,
},
])
}
Expand Down
28 changes: 0 additions & 28 deletions src/api/sources/modrinth/ratelimit.rs

This file was deleted.

29 changes: 28 additions & 1 deletion src/api/sources/vanilla/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::HashMap;

use anyhow::{anyhow, bail, Result};

use crate::api::{app::App, models::Environment, step::Step};
use crate::api::{app::App, models::Environment, step::{CacheLocation, FileMeta, Step}, utils::hashing::HashFormat};

mod assets;
mod manifest;
Expand All @@ -9,6 +11,31 @@ mod rulematcher;

pub use self::{assets::*, manifest::*, version::*, rulematcher::*};

Check warning on line 12 in src/api/sources/vanilla/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused imports: `assets::*`, `rulematcher::*`

warning: unused imports: `assets::*`, `rulematcher::*` --> src/api/sources/vanilla/mod.rs:12:16 | 12 | pub use self::{assets::*, manifest::*, version::*, rulematcher::*}; | ^^^^^^^^^ ^^^^^^^^^^^^^^

impl VersionInfo {
pub fn into_step(&self, ty: DownloadType) -> Option<Vec<Step>> {

Check failure on line 15 in src/api/sources/vanilla/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

methods called `into_*` usually take `self` by value

error: methods called `into_*` usually take `self` by value --> src/api/sources/vanilla/mod.rs:15:22 | 15 | pub fn into_step(&self, ty: DownloadType) -> Option<Vec<Step>> { | ^^^^^ | = help: consider choosing a less ambiguous name = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention

Check warning on line 15 in src/api/sources/vanilla/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

method `into_step` is never used

warning: method `into_step` is never used --> src/api/sources/vanilla/mod.rs:15:12 | 14 | impl VersionInfo { | ---------------- method in this implementation 15 | pub fn into_step(&self, ty: DownloadType) -> Option<Vec<Step>> { | ^^^^^^^^^
let file = self.downloads.get(&ty)?;

let filename = format!("{}-{ty:?}.jar", self.id);

let metadata = FileMeta {
filename: filename.clone(),
cache: Some(CacheLocation("pistonmeta".into(), filename)),
size: Some(file.size),
hashes: HashMap::from([
(HashFormat::Sha1, file.sha1.clone())
]),
};

Some(vec![
Step::CacheCheck(metadata.clone()),
Step::Download {
url: file.url.clone(),
metadata,
},
])
}
}

pub struct VanillaAPI<'a>(pub &'a App);

Check warning on line 39 in src/api/sources/vanilla/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

struct `VanillaAPI` is never constructed

warning: struct `VanillaAPI` is never constructed --> src/api/sources/vanilla/mod.rs:39:12 | 39 | pub struct VanillaAPI<'a>(pub &'a App); | ^^^^^^^^^^

impl<'a> VanillaAPI<'a> {
Expand Down
22 changes: 0 additions & 22 deletions src/api/sources/vanilla/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::api::{step::{CacheStrategy, Step}, utils::hashing::HashFormat};

use super::VersionType;

#[derive(Debug, Clone, Deserialize, Serialize, Default)]
Expand Down Expand Up @@ -33,26 +31,6 @@ pub struct VersionInfo {
pub release_time: String,
}

impl VersionInfo {
pub fn into_step(&self, ty: DownloadType) -> Option<Vec<Step>> {
let file = self.downloads.get(&ty)?;

let filename = format!("{}-{ty:?}.jar", self.id);

Some(vec![
Step::CacheCheck(CacheStrategy::File { namespace: "pistonmeta".into(), path: filename.clone() }),
Step::Download {
url: file.url.clone(),
filename: filename,
size: Some(file.size),
hashes: HashMap::from([
(HashFormat::Sha1, file.sha1.clone())
]),
},
])
}
}

#[derive(Debug, Clone, Copy, Deserialize, Serialize, Hash, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DownloadType {
Expand Down
Loading

0 comments on commit 6135531

Please sign in to comment.