mirror of https://github.com/Trivernis/nenv
Add downloading to install command
parent
4abc55d7c1
commit
5238fb57e0
@ -1,5 +0,0 @@
|
||||
use crate::{error::LibResult, Version};
|
||||
|
||||
pub async fn download_version(version: Version) -> LibResult<()> {
|
||||
todo!("Download node version to data dir")
|
||||
}
|
@ -1,9 +1,25 @@
|
||||
use std::io;
|
||||
|
||||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::web_api::error::ApiError;
|
||||
|
||||
pub(crate) type LibResult<T> = Result<T>;
|
||||
pub(crate) type LibError = Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {}
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
pub enum Error {
|
||||
#[error("Failed to call nodejs.com api: {0}")]
|
||||
Web(
|
||||
#[from]
|
||||
#[source]
|
||||
#[diagnostic_source]
|
||||
ApiError,
|
||||
),
|
||||
|
||||
#[error("IO Error: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
use repository::{config::Config, NodeVersion, Repository};
|
||||
|
||||
mod consts;
|
||||
mod download;
|
||||
pub mod error;
|
||||
mod utils;
|
||||
pub mod repository;
|
||||
mod web_api;
|
||||
use error::Result;
|
||||
|
||||
pub enum Version {
|
||||
Latest,
|
||||
Lts,
|
||||
Specific(u8, Option<u8>, Option<u16>),
|
||||
pub async fn install_version(version: NodeVersion) -> Result<()> {
|
||||
get_repository().await?.install_version(version).await
|
||||
}
|
||||
|
||||
pub fn install(version: Version) {}
|
||||
async fn get_repository() -> Result<Repository> {
|
||||
Repository::init(Config::default()).await
|
||||
}
|
||||
|
@ -1,11 +1,29 @@
|
||||
use args::Args;
|
||||
use clap::Parser;
|
||||
|
||||
use nenv::repository::NodeVersion;
|
||||
|
||||
mod args;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
color_eyre::install().unwrap();
|
||||
let args: Args = Args::parse();
|
||||
dbg!(args);
|
||||
match args.commmand {
|
||||
args::Command::Install(v) => nenv::install_version(version_to_req(v.version))
|
||||
.await
|
||||
.unwrap(),
|
||||
args::Command::Use(_) => todo!(),
|
||||
args::Command::Default => todo!(),
|
||||
args::Command::Version => todo!(),
|
||||
};
|
||||
}
|
||||
|
||||
fn version_to_req(version: args::Version) -> NodeVersion {
|
||||
match version {
|
||||
args::Version::Latest => NodeVersion::Latest,
|
||||
args::Version::LatestLts => NodeVersion::LatestLts,
|
||||
args::Version::Req(req) => NodeVersion::Req(req),
|
||||
args::Version::Lts(lts_name) => NodeVersion::Lts(lts_name),
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
pub struct Config {
|
||||
pub dist_base_url: String,
|
||||
pub default_version: String,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dist_base_url: String::from("https://nodejs.org/dist"),
|
||||
default_version: String::from("latest"),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use semver::{Version, VersionReq};
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
io::BufWriter,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
consts::{BIN_DIR, CACHE_DIR, CFG_DIR, DATA_DIR, NODE_ARCHIVE_SUFFIX, NODE_VERSIONS_DIR},
|
||||
error::LibResult,
|
||||
web_api::{VersionInfo, WebApi},
|
||||
};
|
||||
|
||||
use self::{config::Config, versions::Versions};
|
||||
|
||||
pub mod config;
|
||||
pub mod versions;
|
||||
|
||||
pub enum NodeVersion {
|
||||
Latest,
|
||||
LatestLts,
|
||||
Lts(String),
|
||||
Req(VersionReq),
|
||||
}
|
||||
|
||||
pub struct Repository {
|
||||
versions: Versions,
|
||||
web_api: WebApi,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Repository {
|
||||
/// Initializes a new repository with the given confi
|
||||
pub async fn init(config: Config) -> LibResult<Self> {
|
||||
Self::create_folders().await?;
|
||||
let web_api = WebApi::new(&config.dist_base_url);
|
||||
let all_versions = web_api.get_versions().await?;
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
web_api,
|
||||
versions: Versions::new(all_versions),
|
||||
})
|
||||
}
|
||||
|
||||
async fn create_folders() -> LibResult<()> {
|
||||
let dirs = vec![
|
||||
&*CFG_DIR,
|
||||
&*DATA_DIR,
|
||||
&*CACHE_DIR,
|
||||
&*BIN_DIR,
|
||||
&*NODE_VERSIONS_DIR,
|
||||
];
|
||||
for dir in dirs {
|
||||
if !dir.exists() {
|
||||
fs::create_dir_all(dir).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn install_version(&self, version_req: NodeVersion) -> LibResult<()> {
|
||||
let info = self.parse_req(version_req);
|
||||
let archive_path = self.download_version(&info.version).await?;
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn download_version(&self, version: &Version) -> LibResult<PathBuf> {
|
||||
let download_path = CACHE_DIR.join(format!("node-v{}{}", version, *NODE_ARCHIVE_SUFFIX));
|
||||
let mut download_writer = BufWriter::new(File::create(&download_path).await?);
|
||||
self.web_api
|
||||
.download_version(version.to_string(), &mut download_writer)
|
||||
.await?;
|
||||
|
||||
Ok(download_path)
|
||||
}
|
||||
|
||||
fn parse_req(&self, version_req: NodeVersion) -> &VersionInfo {
|
||||
match version_req {
|
||||
NodeVersion::Latest => self.versions.latest(),
|
||||
NodeVersion::LatestLts => self.versions.latest_lts(),
|
||||
NodeVersion::Lts(lts) => self.versions.get_lts(<s).expect("Version not found"),
|
||||
NodeVersion::Req(req) => self
|
||||
.versions
|
||||
.get_fulfilling(&req)
|
||||
.expect("Version not found"),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
use crate::web_api::VersionInfo;
|
||||
|
||||
pub struct Versions {
|
||||
lts_versions: HashMap<String, VersionReq>,
|
||||
versions: HashMap<Version, VersionInfo>,
|
||||
}
|
||||
|
||||
impl Versions {
|
||||
/// creates a new instance to access version information
|
||||
pub fn new(all_versions: Vec<VersionInfo>) -> Self {
|
||||
let lts_versions = all_versions
|
||||
.iter()
|
||||
.filter_map(|v| {
|
||||
Some((
|
||||
v.lts.as_ref()?.to_lowercase(),
|
||||
VersionReq::parse(&format!("{}", v.version.major)).ok()?,
|
||||
))
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
let versions = all_versions
|
||||
.into_iter()
|
||||
.map(|v| (v.version.to_owned(), v))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Self {
|
||||
lts_versions,
|
||||
versions,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the latest known node version
|
||||
pub fn latest(&self) -> &VersionInfo {
|
||||
let mut versions = self.versions.keys().collect::<Vec<_>>();
|
||||
versions.sort();
|
||||
|
||||
self.versions
|
||||
.get(versions.last().expect("No known node versions"))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the latest node lts version
|
||||
pub fn latest_lts(&self) -> &VersionInfo {
|
||||
let mut versions = self
|
||||
.lts_versions
|
||||
.values()
|
||||
.filter_map(|req| self.get_fulfilling(req))
|
||||
.collect::<Vec<_>>();
|
||||
versions.sort_by_key(|v| &v.version);
|
||||
versions.last().expect("No known lts node versions")
|
||||
}
|
||||
|
||||
/// Returns a lts version by name
|
||||
pub fn get_lts<S: AsRef<str>>(&self, lts_name: S) -> Option<&VersionInfo> {
|
||||
let lts_version = self.lts_versions.get(lts_name.as_ref())?;
|
||||
self.get_fulfilling(lts_version)
|
||||
}
|
||||
|
||||
/// Returns any version that fulfills the given requirement
|
||||
pub fn get_fulfilling(&self, req: &VersionReq) -> Option<&VersionInfo> {
|
||||
let mut versions = self
|
||||
.versions
|
||||
.keys()
|
||||
.filter(|v| req.matches(v))
|
||||
.collect::<Vec<_>>();
|
||||
versions.sort();
|
||||
|
||||
self.versions.get(versions.last()?)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
use std::{borrow::Borrow, collections::HashMap, hash::Hash};
|
||||
|
||||
use crate::web_api::VersionInfo;
|
||||
|
||||
/// Converts the list of versions to a tree for easy version lookup
|
||||
pub fn convert_version_list_to_tree(
|
||||
version_infos: Vec<VersionInfo>,
|
||||
) -> HashMap<u64, HashMap<u64, HashMap<u64, VersionInfo>>> {
|
||||
let mut version_map = HashMap::new();
|
||||
|
||||
for info in version_infos {
|
||||
let major_map = version_map.get_mut_or_insert(info.version.major, HashMap::new());
|
||||
let minor_map = major_map.get_mut_or_insert(info.version.minor, HashMap::new());
|
||||
minor_map.insert(info.version.patch, info);
|
||||
}
|
||||
|
||||
version_map
|
||||
}
|
||||
|
||||
trait GetOrInsert<K: Copy, V> {
|
||||
fn get_mut_or_insert(&mut self, key: K, default_value: V) -> &mut V;
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash + Copy, V> GetOrInsert<K, V> for HashMap<K, V> {
|
||||
fn get_mut_or_insert(&mut self, key: K, default_value: V) -> &mut V {
|
||||
if !self.contains_key(&key) {
|
||||
self.insert(key, default_value);
|
||||
}
|
||||
self.get_mut(&key).unwrap()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue