pesde/src/lib.rs

309 lines
9.4 KiB
Rust
Raw Normal View History

2024-08-03 21:18:38 +01:00
#![deny(missing_docs)]
2024-09-28 23:04:35 +01:00
//! A package manager for the Luau programming language, supporting multiple runtimes including Roblox and Lune.
//! pesde has its own registry, however it can also use Wally, and Git repositories as package sources.
2024-08-03 21:18:38 +01:00
//! It has been designed with multiple targets in mind, namely Roblox, Lune, and Luau.
2024-07-12 23:09:37 +01:00
2024-11-10 15:43:25 +00:00
use crate::{
lockfile::Lockfile,
manifest::Manifest,
source::{traits::PackageSource, PackageSources},
};
2024-11-05 19:44:24 +00:00
use async_stream::stream;
use fs_err::tokio as fs;
2024-11-10 15:43:25 +00:00
use futures::{future::try_join_all, Stream};
2024-08-12 21:28:37 +01:00
use gix::sec::identity::Account;
use std::{
2024-11-10 15:43:25 +00:00
collections::{HashMap, HashSet},
2024-08-12 21:28:37 +01:00
path::{Path, PathBuf},
};
2024-07-12 23:09:37 +01:00
2024-08-03 21:18:38 +01:00
/// Downloading packages
2024-07-17 18:38:01 +01:00
pub mod download;
2024-08-03 21:18:38 +01:00
/// Linking packages
2024-07-17 18:38:01 +01:00
pub mod linking;
2024-08-03 21:18:38 +01:00
/// Lockfile
2024-07-12 23:09:37 +01:00
pub mod lockfile;
2024-08-03 21:18:38 +01:00
/// Manifest
2024-03-04 20:18:49 +00:00
pub mod manifest;
2024-08-03 21:18:38 +01:00
/// Package names
2024-07-12 23:09:37 +01:00
pub mod names;
2024-08-03 21:18:38 +01:00
/// Patching packages
2024-07-23 23:53:34 +01:00
#[cfg(feature = "patches")]
2024-07-23 15:37:47 +01:00
pub mod patches;
2024-08-03 21:18:38 +01:00
/// Resolving packages
pub mod resolver;
2024-08-03 21:18:38 +01:00
/// Running scripts
pub mod scripts;
2024-08-03 21:18:38 +01:00
/// Package sources
2024-07-12 23:09:37 +01:00
pub mod source;
2024-07-22 21:00:09 +01:00
pub(crate) mod util;
2024-07-12 23:09:37 +01:00
2024-08-03 21:18:38 +01:00
/// The name of the manifest file
pub const MANIFEST_FILE_NAME: &str = "pesde.toml";
2024-08-03 21:18:38 +01:00
/// The name of the lockfile
2024-07-12 23:09:37 +01:00
pub const LOCKFILE_FILE_NAME: &str = "pesde.lock";
2024-08-03 21:18:38 +01:00
/// The name of the default index
pub const DEFAULT_INDEX_NAME: &str = "default";
2024-08-03 21:18:38 +01:00
/// The name of the packages container
2024-07-17 18:38:01 +01:00
pub const PACKAGES_CONTAINER_NAME: &str = ".pesde";
2024-08-08 16:59:59 +01:00
pub(crate) const LINK_LIB_NO_FILE_FOUND: &str = "____pesde_no_export_file_found";
2024-07-12 23:09:37 +01:00
2024-08-03 21:18:38 +01:00
/// Struct containing the authentication configuration
2024-07-12 23:09:37 +01:00
#[derive(Debug, Default, Clone)]
pub struct AuthConfig {
tokens: HashMap<gix::Url, String>,
2024-08-12 21:28:37 +01:00
git_credentials: Option<Account>,
2024-07-12 23:09:37 +01:00
}
impl AuthConfig {
2024-08-03 21:18:38 +01:00
/// Create a new `AuthConfig`
2024-07-12 23:09:37 +01:00
pub fn new() -> Self {
AuthConfig::default()
}
/// Set the tokens
pub fn with_tokens<I: IntoIterator<Item = (gix::Url, S)>, S: AsRef<str>>(
2024-08-12 21:28:37 +01:00
mut self,
tokens: I,
) -> Self {
self.tokens = tokens
2024-08-12 21:28:37 +01:00
.into_iter()
.map(|(url, s)| (url, s.as_ref().to_string()))
.collect();
2024-07-12 23:09:37 +01:00
self
}
2024-08-03 21:18:38 +01:00
/// Set the git credentials
2024-08-12 21:28:37 +01:00
pub fn with_git_credentials(mut self, git_credentials: Option<Account>) -> Self {
2024-07-22 15:41:45 +01:00
self.git_credentials = git_credentials;
self
2024-07-12 23:09:37 +01:00
}
2024-08-12 21:28:37 +01:00
/// Get the tokens
pub fn tokens(&self) -> &HashMap<gix::Url, String> {
&self.tokens
2024-08-12 21:28:37 +01:00
}
/// Get the git credentials
pub fn git_credentials(&self) -> Option<&Account> {
self.git_credentials.as_ref()
}
2024-07-12 23:09:37 +01:00
}
2024-08-03 21:18:38 +01:00
/// The main struct of the pesde library, representing a project
#[derive(Debug, Clone)]
2024-07-12 23:09:37 +01:00
pub struct Project {
package_dir: PathBuf,
workspace_dir: Option<PathBuf>,
2024-07-12 23:09:37 +01:00
data_dir: PathBuf,
auth_config: AuthConfig,
2024-07-28 17:19:54 +01:00
cas_dir: PathBuf,
2024-07-12 23:09:37 +01:00
}
impl Project {
2024-08-03 21:18:38 +01:00
/// Create a new `Project`
pub fn new<P: AsRef<Path>, Q: AsRef<Path>, R: AsRef<Path>, S: AsRef<Path>>(
package_dir: P,
workspace_dir: Option<Q>,
data_dir: R,
cas_dir: S,
2024-07-12 23:09:37 +01:00
auth_config: AuthConfig,
) -> Self {
Project {
package_dir: package_dir.as_ref().to_path_buf(),
workspace_dir: workspace_dir.map(|d| d.as_ref().to_path_buf()),
2024-07-12 23:09:37 +01:00
data_dir: data_dir.as_ref().to_path_buf(),
auth_config,
2024-07-28 17:19:54 +01:00
cas_dir: cas_dir.as_ref().to_path_buf(),
2024-07-12 23:09:37 +01:00
}
}
/// The directory of the package
pub fn package_dir(&self) -> &Path {
&self.package_dir
}
/// The directory of the workspace this package belongs to, if any
pub fn workspace_dir(&self) -> Option<&Path> {
self.workspace_dir.as_deref()
2024-07-12 23:09:37 +01:00
}
/// The directory to store general-purpose data
2024-07-12 23:09:37 +01:00
pub fn data_dir(&self) -> &Path {
&self.data_dir
}
/// The authentication configuration
2024-07-22 15:41:45 +01:00
pub fn auth_config(&self) -> &AuthConfig {
&self.auth_config
}
/// The CAS (content-addressable storage) directory
2024-07-28 17:19:54 +01:00
pub fn cas_dir(&self) -> &Path {
&self.cas_dir
}
2024-08-03 21:18:38 +01:00
/// Read the manifest file
2024-11-05 19:44:24 +00:00
pub async fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
let string = fs::read_to_string(self.package_dir.join(MANIFEST_FILE_NAME)).await?;
2024-07-23 15:37:47 +01:00
Ok(string)
2024-07-12 23:09:37 +01:00
}
2024-08-03 21:18:38 +01:00
/// Deserialize the manifest file
2024-11-05 19:44:24 +00:00
pub async fn deser_manifest(&self) -> Result<Manifest, errors::ManifestReadError> {
let string = fs::read_to_string(self.package_dir.join(MANIFEST_FILE_NAME)).await?;
Ok(toml::from_str(&string)?)
2024-07-12 23:09:37 +01:00
}
2024-08-03 21:18:38 +01:00
/// Write the manifest file
2024-11-05 19:44:24 +00:00
pub async fn write_manifest<S: AsRef<[u8]>>(&self, manifest: S) -> Result<(), std::io::Error> {
fs::write(self.package_dir.join(MANIFEST_FILE_NAME), manifest.as_ref()).await
2024-07-12 23:09:37 +01:00
}
2024-08-03 21:18:38 +01:00
/// Deserialize the lockfile
2024-11-05 19:44:24 +00:00
pub async fn deser_lockfile(&self) -> Result<Lockfile, errors::LockfileReadError> {
let string = fs::read_to_string(self.package_dir.join(LOCKFILE_FILE_NAME)).await?;
Ok(toml::from_str(&string)?)
}
2024-08-03 21:18:38 +01:00
/// Write the lockfile
2024-11-05 19:44:24 +00:00
pub async fn write_lockfile(
&self,
lockfile: Lockfile,
) -> Result<(), errors::LockfileWriteError> {
let string = toml::to_string(&lockfile)?;
2024-11-05 19:44:24 +00:00
fs::write(self.package_dir.join(LOCKFILE_FILE_NAME), string).await?;
Ok(())
}
/// Get the workspace members
2024-11-05 19:44:24 +00:00
pub async fn workspace_members<P: AsRef<Path>>(
&self,
dir: P,
2024-11-05 19:44:24 +00:00
) -> Result<
impl Stream<Item = Result<(PathBuf, Manifest), errors::WorkspaceMembersError>>,
errors::WorkspaceMembersError,
> {
let dir = dir.as_ref().to_path_buf();
2024-11-05 19:44:24 +00:00
let manifest = fs::read_to_string(dir.join(MANIFEST_FILE_NAME))
.await
2024-11-01 19:57:32 +00:00
.map_err(errors::WorkspaceMembersError::ManifestMissing)?;
2024-09-28 23:37:38 +01:00
let manifest = toml::from_str::<Manifest>(&manifest).map_err(|e| {
errors::WorkspaceMembersError::ManifestDeser(dir.to_path_buf(), Box::new(e))
})?;
let members = manifest
.workspace_members
.into_iter()
.map(|glob| dir.join(glob))
.map(|path| glob::glob(&path.as_os_str().to_string_lossy()))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flat_map(|paths| paths.into_iter())
.collect::<Result<Vec<_>, _>>()?;
2024-11-05 19:44:24 +00:00
Ok(stream! {
for path in members {
let manifest = fs::read_to_string(path.join(MANIFEST_FILE_NAME))
.await
2024-11-01 19:57:32 +00:00
.map_err(errors::WorkspaceMembersError::ManifestMissing)?;
2024-09-28 23:37:38 +01:00
let manifest = toml::from_str::<Manifest>(&manifest).map_err(|e| {
errors::WorkspaceMembersError::ManifestDeser(path.clone(), Box::new(e))
})?;
2024-11-05 19:44:24 +00:00
yield Ok((path, manifest));
}
})
}
2024-07-12 23:09:37 +01:00
}
2024-11-10 15:43:25 +00:00
/// Refreshes the sources asynchronously
pub async fn refresh_sources<I: Iterator<Item = PackageSources>>(
project: &Project,
sources: I,
refreshed_sources: &mut HashSet<PackageSources>,
) -> Result<(), Box<source::errors::RefreshError>> {
try_join_all(sources.map(|source| {
let needs_refresh = refreshed_sources.insert(source.clone());
async move {
if needs_refresh {
source.refresh(project).await.map_err(Box::new)
} else {
Ok(())
}
}
}))
.await
.map(|_| ())
}
/// Errors that can occur when using the pesde library
2024-07-12 23:09:37 +01:00
pub mod errors {
use std::path::PathBuf;
2024-07-12 23:09:37 +01:00
use thiserror::Error;
2024-08-03 21:18:38 +01:00
/// Errors that can occur when reading the manifest file
2024-07-12 23:09:37 +01:00
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ManifestReadError {
2024-08-03 21:18:38 +01:00
/// An IO error occurred
2024-07-12 23:09:37 +01:00
#[error("io error reading manifest file")]
Io(#[from] std::io::Error),
2024-08-03 21:18:38 +01:00
/// An error occurred while deserializing the manifest file
2024-07-12 23:09:37 +01:00
#[error("error deserializing manifest file")]
Serde(#[from] toml::de::Error),
2024-07-12 23:09:37 +01:00
}
2024-08-03 21:18:38 +01:00
/// Errors that can occur when reading the lockfile
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LockfileReadError {
2024-08-03 21:18:38 +01:00
/// An IO error occurred
#[error("io error reading lockfile")]
Io(#[from] std::io::Error),
2024-08-03 21:18:38 +01:00
/// An error occurred while deserializing the lockfile
#[error("error deserializing lockfile")]
Serde(#[from] toml::de::Error),
}
2024-08-03 21:18:38 +01:00
/// Errors that can occur when writing the lockfile
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LockfileWriteError {
2024-08-03 21:18:38 +01:00
/// An IO error occurred
#[error("io error writing lockfile")]
Io(#[from] std::io::Error),
2024-08-03 21:18:38 +01:00
/// An error occurred while serializing the lockfile
#[error("error serializing lockfile")]
Serde(#[from] toml::ser::Error),
}
/// Errors that can occur when finding workspace members
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum WorkspaceMembersError {
/// The manifest file could not be found
2024-11-01 19:57:32 +00:00
#[error("missing manifest file")]
ManifestMissing(#[source] std::io::Error),
/// An error occurred deserializing the manifest file
#[error("error deserializing manifest file at {0}")]
2024-09-28 23:37:38 +01:00
ManifestDeser(PathBuf, #[source] Box<toml::de::Error>),
/// An error occurred interacting with the filesystem
#[error("error interacting with the filesystem")]
Io(#[from] std::io::Error),
/// An invalid glob pattern was found
#[error("invalid glob pattern")]
Glob(#[from] glob::PatternError),
/// An error occurred while globbing
#[error("error globbing")]
Globbing(#[from] glob::GlobError),
}
}