use std::path::{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(crate) mod extract; 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::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(()) } /// Installs a specified node version 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?; self.extract_archive(info, &archive_path)?; Ok(()) } async fn download_version(&self, version: &Version) -> LibResult { let download_path = CACHE_DIR.join(format!("node-v{}{}", version, *NODE_ARCHIVE_SUFFIX)); if download_path.exists() { return Ok(download_path); } 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 extract_archive(&self, info: &VersionInfo, archive_path: &Path) -> LibResult<()> { let dst_path = NODE_VERSIONS_DIR.join(info.version.to_string()); extract::extract_file(archive_path, &dst_path)?; Ok(()) } 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"), } } }