Skip to content

Commit

Permalink
#4 downloading works
Browse files Browse the repository at this point in the history
  • Loading branch information
hlafaille committed Mar 19, 2024
1 parent 2618a9f commit ca3e8f0
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 9 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ walkdir = "2.5.0"
reqwest = "0.11.26"
tokio = { version = "1.36.0", features = ["full"] }
serde_json = "1.0.114"
hex = "0.4.3"
sha2 = "0.10.8"
3 changes: 3 additions & 0 deletions src/backend/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub struct AbsoltuePaths {
pub config: String,
/// Path to the directory that contains inner working files (ex: state_lockfile, dependency jars, etc)
pub inner_workings: String,
/// Path to the directory containing downloaded dependencies.
pub dependencies: String,
/// Path to the state lockfile within the currently loaded project.
pub state_lockfile: String,
}
Expand Down Expand Up @@ -78,6 +80,7 @@ pub fn get_absolute_paths(debug_mode: &bool) -> io::Result<AbsoltuePaths> {
source: cwd_string.clone() + "/src/java",
config: cwd_string.clone() + "/espresso.toml",
inner_workings: cwd_string.clone() + "/.espresso",
dependencies: cwd_string.clone() + "/.espresso/dependencies",
state_lockfile: cwd_string.clone() + "/.espresso/state.lock.toml"
})
}
Expand Down
44 changes: 42 additions & 2 deletions src/backend/dependency/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::HashMap, error, fmt::format, result};
use serde::{Serialize, Deserialize};

use crate::util::error::EspressoError;
use crate::{backend::context::ProjectContext, frontend::terminal::print_err, util::{self, error::EspressoError, net::download_file}};

/// Represents a resolved dependency
#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -49,7 +49,7 @@ pub enum Flags {
///
/// # Returns
/// Propagated errors, returns a `Vec` of `Package` struct(s). The `Vec` will be empty if no packages were returned in the query.
pub async fn query(q: String) -> result::Result<Vec<Package>, Box<dyn error::Error>> {
pub async fn query(q: &String) -> result::Result<Vec<Package>, Box<dyn error::Error>> {
let client = reqwest::Client::new();

// make a request to the registry
Expand All @@ -69,4 +69,44 @@ pub async fn query(q: String) -> result::Result<Vec<Package>, Box<dyn error::Err
}
};
Ok(query_packages_response.packages)
}

/// Download the latest version of a package
async fn download(p_ctx: &ProjectContext, package: &Package) -> result::Result<(), Box<dyn error::Error>> {
// get the latest version of this project
let version = match package.metadata.versions.get(0) {
Some(v) => v,
None => {
return Err(EspressoError::nib("Failed to get the latest version of the package (there are no versions)"))
}
};

// establish our full path
let download_path = p_ctx.absolute_paths.dependencies.clone() + format!("/{}.jar", version.sha512sum).as_str();

// TODO
// download the file
download_file(&version.artifact_url, &download_path).await?;

// ensure integrity
util::pathutil::ensure_integrity_sha512(&download_path, &version.sha512sum).await?;

Ok(())
}

/// Download the latest version of the package, adding it to the state.lock.toml & cargoespresso.toml file(s)
///
/// # Arguments
/// * `p_ctx` Reference to a `ProjectContext` struct
/// * `package` Reference to the `Package` to be added
///
/// # Returns
/// Propagated `error::Error`
pub async fn add(p_ctx: &ProjectContext, package: &Package) -> result::Result<(), Box<dyn error::Error>> {
// download the package
download(p_ctx, package).await?;

// perform

Ok(())
}
3 changes: 2 additions & 1 deletion src/backend/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ fn ensure_environment(ap: &AbsoltuePaths, debug_mode: &bool) -> io::Result<()>{
fs::create_dir(&ap.project)?
}

// create the inner workings dir
// create the inner workings dirs
std::fs::create_dir_all(&ap.inner_workings)?;
std::fs::create_dir_all(&ap.dependencies)?;

Ok(())
}
Expand Down
26 changes: 21 additions & 5 deletions src/frontend/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::backend::toolchain::{compile_project, run_jar, ToolchainContext};
use crate::backend::{self, context};
use crate::frontend::terminal::{print_err, print_sameline};
use crate::util::error::EspressoError;
use std::fmt::format;
use std::{error, io, result};

use super::terminal::print_general;
Expand Down Expand Up @@ -112,17 +113,18 @@ pub async fn add(
tc_ctx: ToolchainContext,
q: String
) -> result::Result<(ProjectContext, ToolchainContext), Box<dyn error::Error>> {
print_general(format!("Searching for '{}'", q).as_str());
let packages = backend::dependency::resolve::query(q).await?;
for (elem, package) in packages.iter().enumerate() {
print_general(format!("{}) {}:{}", elem + 1, package.group_id, package.artifact_id).as_str());
}
let packages = backend::dependency::resolve::query(&q).await?;

// collect the package selection if there was more than one returned package
let selected_package: &Package;
if packages.len() == 1 {
selected_package = packages.get(0).expect("At least one package was simultaneously returned & not returned. Schrodinger's package..?");
} else if packages.len() > 1 {
print_general(format!("Searching for '{}'", &q).as_str());
for (elem, package) in packages.iter().enumerate() {
print_general(format!("{}) {}:{}", elem + 1, package.group_id, package.artifact_id).as_str());
}

// get the selected package as a string
let mut package_number_selection = String::new();
print_sameline(format!("Select a package (1-{}): ", packages.len()).as_str());
Expand Down Expand Up @@ -155,6 +157,20 @@ pub async fn add(
panic!()
}

// download the package
print_general(format!("Downloading '{}'", selected_package.artifact_id).as_str());
match backend::dependency::resolve::add(&p_ctx, selected_package).await {
Ok(()) => {},
Err(e) => {
print_err(format!("Failed to add package: {}", e).as_str());
panic!()
}
}

// add the package to state.lock.toml

print_general("Package added");

// pass ownership back
Ok((p_ctx, tc_ctx))
}
29 changes: 28 additions & 1 deletion src/util/pathutil.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::path::Path;
use std::{error, path::Path, result};
use tokio::fs;
use sha2::{Digest, Sha512};
use super::error::EspressoError;

/// If a path on the filesystem exists
///
Expand All @@ -10,3 +13,27 @@ use std::path::Path;
pub fn does_path_exist(path: &String) -> bool {
Path::exists(Path::new(path))
}

/// Ensure integirty of a file
///
/// # Arguments
/// * `path`: Reference to a `String` containing the path of the file to check
/// * `expected_sha512_hex`: SHA512 hexadecimal string to compare against
pub async fn ensure_integrity_sha512(
path: &String,
expected_sha512_hex: &String,
) -> result::Result<(), Box<dyn error::Error>> {
// read our file
let contents = fs::read(path).await?;

// hash it to sha512
let mut hasher = Sha512::new();
hasher.update(contents);
let sha512hex = hex::encode(hasher.finalize().to_vec());

// if the expected sha512 hex doesn't equal what we calculated, throw an error
if *expected_sha512_hex != *sha512hex {
return Err(EspressoError::nib(format!("Downloaded file does not have the same SHA512 checksum as defined by the package: Expected={}: Actual={}", expected_sha512_hex, sha512hex).as_str()));
}
Ok(())
}

0 comments on commit ca3e8f0

Please sign in to comment.