Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use install_with_target and export as public from huak_python_manager #786

Merged
merged 1 commit into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion crates/huak_python_manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ anyhow = "1.0.75"
clap = { workspace = true }
colored = { workspace = true }
hex = "0.4.3"
huak_home = { version = "0.0.0", path = "../huak_home" }
human-panic = { workspace = true }
reqwest = { version = "0.11.22", features = ["blocking", "json"] }
sha2 = "0.10.8"
Expand Down
2 changes: 1 addition & 1 deletion crates/huak_python_manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

> This crate is a work-in-progress.

A Python interpreter management system for Huak.
A Python interpreter management.
26 changes: 18 additions & 8 deletions crates/huak_python_manager/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::resolve::RequestedVersion;
use anyhow::Error;
use clap::{Parser, Subcommand};
use huak_python_manager::RequestedVersion;
use std::path::PathBuf;

/// A Python interpreter management system for Huak.
#[derive(Parser)]
Expand All @@ -17,7 +18,7 @@ pub(crate) struct Cli {
impl Cli {
pub(crate) fn run(self) -> Result<(), Error> {
match self.command {
Commands::Install { version } => cmd::install(version),
Commands::Install { version, target } => cmd::install(version, target),
}
}
}
Expand All @@ -29,20 +30,29 @@ enum Commands {
/// Install a Python interpreter.
Install {
#[arg(required = true)]
/// Version of Python to install.
version: RequestedVersion,

/// Target path to install Python to.
#[arg(long, required = true)]
target: PathBuf,
},
}

mod cmd {
use std::path::PathBuf;

use super::{Error, RequestedVersion};
use crate::install::install_to_home;
use crate::resolve::{Options, Strategy};
use huak_python_manager::{install_with_target, Options, Strategy};

pub(crate) fn install(version: RequestedVersion) -> Result<(), Error> {
println!("installing Python {version}");
install_to_home(&Strategy::Selection(Options {
pub(crate) fn install(version: RequestedVersion, target: PathBuf) -> Result<(), Error> {
println!("installing Python {version}...");

let strategy = Strategy::Selection(Options {
version: Some(version),
..Default::default()
}))
});

install_with_target(&strategy, target)
}
}
10 changes: 3 additions & 7 deletions crates/huak_python_manager/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ use crate::{
resolve::{resolve_release, Strategy},
};
use anyhow::{bail, Context, Error, Ok}; // TODO(cnpryer): Use thiserror in library code.
use huak_home::huak_home_dir;
use sha2::{Digest, Sha256};
use std::path::PathBuf;
use tar::Archive;
use zstd::stream::read::Decoder;

pub(crate) fn install_to_home(strategy: &Strategy) -> Result<(), Error> {
pub fn install_with_target<T: Into<PathBuf>>(strategy: &Strategy, target: T) -> Result<(), Error> {
let release = resolve_release(strategy).context("requested release data")?;
let target_dir = huak_home_dir()
.context("requested huak's home directory")?
.join("toolchains")
.join(format!("huak-{}-{}", release.kind, release.version));

let buffer = download_release(&release)?;
validate_checksum(&buffer, release.checksum)?;
Expand All @@ -22,7 +18,7 @@ pub(crate) fn install_to_home(strategy: &Strategy) -> Result<(), Error> {
let decoded = Decoder::with_buffer(buffer.as_slice())?;
let mut archive = Archive::new(decoded);

Ok(archive.unpack(target_dir)?)
Ok(archive.unpack(target.into())?)
}

fn download_release(release: &Release) -> Result<Vec<u8>, Error> {
Expand Down
44 changes: 44 additions & 0 deletions crates/huak_python_manager/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! # Python Manager
//!
//! A Python interpreter management system.
//!
//! NOTE: This crate is a work-in-progress.
//!
//! ## Usage
//!
//! ### CLI
//!
//! ```
//! huak_python_manager install 3.12 --target <path-to-target-dir>
//! ```
//!
//! ### Rust
//!
//! #### Install a Python release to a target directory with minimal configuration.
//!
//! TODO(cnpryer): Design better API.
//!
//! ```rust
//! use std::path::PathBuf;
//! use huak_python_manager::{Options, Strategy, install_with_target};
//!
//!
//! // The version of the Python to install.
//! let version = RequestedVersion::from_str("3.12").unwrap();
//!
//! // Target directory to install Python to.
//! let target = PathBuf::from("...");
//!
//! // Use selection strategy to resolve for the best matching release available.
//! let strategy = Strategy::Selection(Options { version, kind: "cpython", os: "apple", architecture: "aarch64", build_configuration: "pgo+lto"});
//!
//! install_with_target(strategy, target).unwrap();
//!
//! ```

pub use crate::install::install_with_target;
pub use crate::resolve::{Options, RequestedVersion, Strategy};

mod install;
mod releases;
mod resolve;
5 changes: 1 addition & 4 deletions crates/huak_python_manager/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use crate::cli::Cli;
use clap::Parser;
use cli::Cli;
use colored::Colorize;
use human_panic::setup_panic;

mod cli;
mod install;
mod releases;
mod resolve;

fn main() {
setup_panic!();
Expand Down
147 changes: 38 additions & 109 deletions crates/huak_python_manager/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,126 +44,55 @@ fn resolve_release_with_options(options: &Options) -> Option<Release<'static>> {

#[derive(Default)]
/// The strategy used for resolving a Python releases.
pub(crate) enum Strategy {
pub enum Strategy<'a> {
#[default]
/// Resolve with the latest possible Python release version for the current environment.
Latest,
/// `Selection` - Use some selection criteria to determine the Python release. Unused
/// options criteria will resolve to *best possible defaults*.
Selection(Options),
}

#[derive(Default, Debug)]
/// Options criteria used for resolving Python releases.
pub(crate) struct Options {
pub kind: ReleaseKind,
pub version: Option<RequestedVersion>, // TODO(cnpryer): Can this default to something like *Latest*?
pub os: ReleaseOS,
pub architecture: ReleaseArchitecture,
pub build_configuration: ReleaseBuildConfiguration,
Selection(Options<'a>),
}

#[derive(Debug)]
pub(crate) struct ReleaseKind(String);

impl Default for ReleaseKind {
fn default() -> Self {
Self(String::from("cpython"))
}
}

impl PartialEq<str> for ReleaseKind {
fn eq(&self, other: &str) -> bool {
self.0.as_str() == other
}
}

impl PartialEq<ReleaseKind> for &str {
fn eq(&self, other: &ReleaseKind) -> bool {
self == &other.0.as_str()
}
}

#[derive(Debug)]
pub(crate) struct ReleaseOS(String);

impl Default for ReleaseOS {
fn default() -> Self {
Self(String::from(match OS {
"macos" => "apple",
"windows" => "windows",
_ => "linux",
}))
}
}

impl PartialEq<str> for ReleaseOS {
fn eq(&self, other: &str) -> bool {
self.0.as_str() == other
}
}

impl PartialEq<ReleaseOS> for &str {
fn eq(&self, other: &ReleaseOS) -> bool {
self == &other.0.as_str()
}
}

#[derive(Debug)]
pub(crate) struct ReleaseArchitecture(String);

impl Default for ReleaseArchitecture {
fn default() -> Self {
Self(String::from(match ARCH {
"x86_64" => "x86_64",
"aarch64" => "aarch64",
"x86" => "i686", // TODO(cnpryer): Need to look at other windows releases.
_ => unimplemented!(),
}))
}
}

impl PartialEq<str> for ReleaseArchitecture {
fn eq(&self, other: &str) -> bool {
self.0.as_str() == other
}
}

impl PartialEq<ReleaseArchitecture> for &str {
fn eq(&self, other: &ReleaseArchitecture) -> bool {
self == &other.0.as_str()
}
/// Options criteria used for resolving Python releases.
pub struct Options<'a> {
pub kind: &'a str,
pub version: Option<RequestedVersion>, // TODO(cnpryer): Refactor to default as *latest available*
pub os: &'a str,
pub architecture: &'a str,
pub build_configuration: &'a str,
}

#[derive(Debug)]
pub(crate) struct ReleaseBuildConfiguration(String);

impl Default for ReleaseBuildConfiguration {
// TODO(cnpryer): Refactor
impl Default for Options<'static> {
fn default() -> Self {
Self(String::from(match OS {
"windows" => "pgo",
_ => "pgo+lto",
}))
}
}

impl PartialEq<str> for ReleaseBuildConfiguration {
fn eq(&self, other: &str) -> bool {
self.0.as_str() == other
}
}

impl PartialEq<ReleaseBuildConfiguration> for &str {
fn eq(&self, other: &ReleaseBuildConfiguration) -> bool {
self == &other.0.as_str()
Self {
kind: "cpython",
version: Option::default(),
os: match OS {
"macos" => "apple",
"windows" => "windows",
_ => "linux",
},
architecture: match ARCH {
"x86_64" => "x86_64",
"aarch64" => "aarch64",
"x86" => "i686", // TODO(cnpryer): Need to look at other windows releases.
_ => unimplemented!(),
},
build_configuration: match OS {
"windows" => "pgo",
_ => "pgo+lto",
},
}
}
}

#[derive(Debug, Clone)]
pub(crate) struct RequestedVersion {
pub(crate) major: Option<u8>,
pub(crate) minor: Option<u8>,
pub(crate) patch: Option<u8>,
pub struct RequestedVersion {
pub major: Option<u8>,
pub minor: Option<u8>,
pub patch: Option<u8>,
}

impl RequestedVersion {
Expand Down Expand Up @@ -223,11 +152,11 @@ mod tests {
#[test]
fn test_selection() {
let resolved_release = resolve_release(&Strategy::Selection(Options {
kind: ReleaseKind("cpython".to_string()),
kind: "cpython",
version: Some(RequestedVersion::from_str("3.8").unwrap()),
os: ReleaseOS("apple".to_string()),
architecture: ReleaseArchitecture("aarch64".to_string()),
build_configuration: ReleaseBuildConfiguration("pgo+lto".to_string()),
os: "apple",
architecture: "aarch64",
build_configuration: "pgo+lto",
}))
.unwrap();

Expand Down
Loading