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 thiserror::Error;
|
||||||
|
|
||||||
|
use crate::web_api::error::ApiError;
|
||||||
|
|
||||||
pub(crate) type LibResult<T> = Result<T>;
|
pub(crate) type LibResult<T> = Result<T>;
|
||||||
pub(crate) type LibError = Error;
|
pub(crate) type LibError = Error;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
pub enum Error {}
|
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 consts;
|
||||||
mod download;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod utils;
|
pub mod repository;
|
||||||
mod web_api;
|
mod web_api;
|
||||||
|
use error::Result;
|
||||||
|
|
||||||
pub enum Version {
|
pub async fn install_version(version: NodeVersion) -> Result<()> {
|
||||||
Latest,
|
get_repository().await?.install_version(version).await
|
||||||
Lts,
|
|
||||||
Specific(u8, Option<u8>, Option<u16>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install(version: Version) {}
|
async fn get_repository() -> Result<Repository> {
|
||||||
|
Repository::init(Config::default()).await
|
||||||
|
}
|
||||||
|
@ -1,11 +1,29 @@
|
|||||||
use args::Args;
|
use args::Args;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
use nenv::repository::NodeVersion;
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
color_eyre::install().unwrap();
|
color_eyre::install().unwrap();
|
||||||
let args: Args = Args::parse();
|
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