mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-12 11:00:36 +00:00
docs: add package docs
This commit is contained in:
parent
e07ec4e859
commit
431c2b634f
22 changed files with 345 additions and 17 deletions
|
@ -29,19 +29,21 @@ impl SelfInstallCommand {
|
|||
.0;
|
||||
let path: String = env.get_value("Path").context("failed to get Path value")?;
|
||||
|
||||
let bin_dir = bin_dir.to_string_lossy();
|
||||
|
||||
let exists = path
|
||||
.split(';')
|
||||
.any(|part| part == bin_dir.to_string_lossy().as_ref());
|
||||
.any(|part| *part == bin_dir);
|
||||
|
||||
if !exists {
|
||||
let new_path = format!("{path};{}", bin_dir.to_string_lossy());
|
||||
let new_path = format!("{path};{bin_dir}");
|
||||
env.set_value("Path", &new_path)
|
||||
.context("failed to set Path value")?;
|
||||
}
|
||||
|
||||
println!(
|
||||
"installed {} {}!",
|
||||
env!("CARGO_PKG_NAME").cyan(),
|
||||
env!("CARGO_BIN_NAME").cyan(),
|
||||
env!("CARGO_PKG_VERSION").yellow(),
|
||||
);
|
||||
|
||||
|
@ -59,13 +61,13 @@ impl SelfInstallCommand {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
println!(
|
||||
r#"installed {} {}! in order to be able to run binary exports as programs, add the following line to your shell profile:
|
||||
r#"installed {} {}! add the following line to your shell profile in order to get the binary and binary exports as executables usable from anywhere:
|
||||
|
||||
{}
|
||||
|
||||
and then restart your shell.
|
||||
"#,
|
||||
env!("CARGO_PKG_NAME").cyan(),
|
||||
env!("CARGO_BIN_NAME").cyan(),
|
||||
env!("CARGO_PKG_VERSION").yellow(),
|
||||
format!(r#"export PATH="$PATH:~/{}/bin""#, HOME_DIR)
|
||||
.bold()
|
||||
|
|
|
@ -20,6 +20,7 @@ type MultithreadDownloadJob = (
|
|||
);
|
||||
|
||||
impl Project {
|
||||
/// Downloads a graph of dependencies
|
||||
pub fn download_graph(
|
||||
&self,
|
||||
graph: &DependencyGraph,
|
||||
|
@ -101,24 +102,31 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when downloading a graph
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when downloading a graph
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum DownloadGraphError {
|
||||
/// Error occurred deserializing the project manifest
|
||||
#[error("error deserializing project manifest")]
|
||||
ManifestDeserializationFailed(#[from] crate::errors::ManifestReadError),
|
||||
|
||||
/// Error occurred refreshing a package source
|
||||
#[error("failed to refresh package source")]
|
||||
RefreshFailed(#[from] Box<crate::source::errors::RefreshError>),
|
||||
|
||||
/// Error interacting with the filesystem
|
||||
#[error("error interacting with filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// Error downloading a package
|
||||
#[error("failed to download package")]
|
||||
DownloadFailed(#[from] crate::source::errors::DownloadError),
|
||||
|
||||
/// Error writing package contents
|
||||
#[error("failed to write package contents")]
|
||||
WriteFailed(std::io::Error),
|
||||
}
|
||||
|
|
46
src/lib.rs
46
src/lib.rs
|
@ -1,4 +1,7 @@
|
|||
// #![deny(missing_docs)] - TODO: bring this back before publishing 0.5
|
||||
#![deny(missing_docs)]
|
||||
//! pesde is a package manager for Luau, designed to be feature-rich and easy to use.
|
||||
//! pesde has its own registry, however it can also use Wally, and GitHub as package sources.
|
||||
//! It has been designed with multiple targets in mind, namely Roblox, Lune, and Luau.
|
||||
|
||||
#[cfg(not(any(feature = "roblox", feature = "lune", feature = "luau")))]
|
||||
compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be enabled");
|
||||
|
@ -6,24 +9,39 @@ compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be
|
|||
use crate::lockfile::Lockfile;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Downloading packages
|
||||
pub mod download;
|
||||
/// Linking packages
|
||||
pub mod linking;
|
||||
/// Lockfile
|
||||
pub mod lockfile;
|
||||
/// Manifest
|
||||
pub mod manifest;
|
||||
/// Package names
|
||||
pub mod names;
|
||||
/// Patching packages
|
||||
#[cfg(feature = "patches")]
|
||||
pub mod patches;
|
||||
/// Resolving packages
|
||||
pub mod resolver;
|
||||
/// Running scripts
|
||||
pub mod scripts;
|
||||
/// Package sources
|
||||
pub mod source;
|
||||
pub(crate) mod util;
|
||||
|
||||
/// The name of the manifest file
|
||||
pub const MANIFEST_FILE_NAME: &str = "pesde.toml";
|
||||
/// The name of the lockfile
|
||||
pub const LOCKFILE_FILE_NAME: &str = "pesde.lock";
|
||||
/// The name of the default index
|
||||
pub const DEFAULT_INDEX_NAME: &str = "default";
|
||||
/// The name of the packages container
|
||||
pub const PACKAGES_CONTAINER_NAME: &str = ".pesde";
|
||||
/// Maximum size of a package's archive
|
||||
pub const MAX_ARCHIVE_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Struct containing the authentication configuration
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct AuthConfig {
|
||||
pesde_token: Option<String>,
|
||||
|
@ -31,23 +49,28 @@ pub struct AuthConfig {
|
|||
}
|
||||
|
||||
impl AuthConfig {
|
||||
/// Create a new `AuthConfig`
|
||||
pub fn new() -> Self {
|
||||
AuthConfig::default()
|
||||
}
|
||||
|
||||
/// Access the pesde token
|
||||
pub fn pesde_token(&self) -> Option<&str> {
|
||||
self.pesde_token.as_deref()
|
||||
}
|
||||
|
||||
/// Access the git credentials
|
||||
pub fn git_credentials(&self) -> Option<&gix::sec::identity::Account> {
|
||||
self.git_credentials.as_ref()
|
||||
}
|
||||
|
||||
/// Set the pesde token
|
||||
pub fn with_pesde_token<S: AsRef<str>>(mut self, token: Option<S>) -> Self {
|
||||
self.pesde_token = token.map(|s| s.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the git credentials
|
||||
pub fn with_git_credentials(
|
||||
mut self,
|
||||
git_credentials: Option<gix::sec::identity::Account>,
|
||||
|
@ -57,6 +80,7 @@ impl AuthConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// The main struct of the pesde library, representing a project
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Project {
|
||||
path: PathBuf,
|
||||
|
@ -66,6 +90,7 @@ pub struct Project {
|
|||
}
|
||||
|
||||
impl Project {
|
||||
/// Create a new `Project`
|
||||
pub fn new<P: AsRef<Path>, Q: AsRef<Path>, R: AsRef<Path>>(
|
||||
path: P,
|
||||
data_dir: Q,
|
||||
|
@ -80,41 +105,50 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Access the path
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Access the data directory
|
||||
pub fn data_dir(&self) -> &Path {
|
||||
&self.data_dir
|
||||
}
|
||||
|
||||
/// Access the authentication configuration
|
||||
pub fn auth_config(&self) -> &AuthConfig {
|
||||
&self.auth_config
|
||||
}
|
||||
|
||||
/// Access the CAS (content-addressable storage) directory
|
||||
pub fn cas_dir(&self) -> &Path {
|
||||
&self.cas_dir
|
||||
}
|
||||
|
||||
/// Read the manifest file
|
||||
pub fn read_manifest(&self) -> Result<String, errors::ManifestReadError> {
|
||||
let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
/// Deserialize the manifest file
|
||||
pub fn deser_manifest(&self) -> Result<manifest::Manifest, errors::ManifestReadError> {
|
||||
let string = std::fs::read_to_string(self.path.join(MANIFEST_FILE_NAME))?;
|
||||
Ok(toml::from_str(&string)?)
|
||||
}
|
||||
|
||||
/// Write the manifest file
|
||||
pub fn write_manifest<S: AsRef<[u8]>>(&self, manifest: S) -> Result<(), std::io::Error> {
|
||||
std::fs::write(self.path.join(MANIFEST_FILE_NAME), manifest.as_ref())
|
||||
}
|
||||
|
||||
/// Deserialize the lockfile
|
||||
pub fn deser_lockfile(&self) -> Result<Lockfile, errors::LockfileReadError> {
|
||||
let string = std::fs::read_to_string(self.path.join(LOCKFILE_FILE_NAME))?;
|
||||
Ok(toml::from_str(&string)?)
|
||||
}
|
||||
|
||||
/// Write the lockfile
|
||||
pub fn write_lockfile(&self, lockfile: Lockfile) -> Result<(), errors::LockfileWriteError> {
|
||||
let string = toml::to_string(&lockfile)?;
|
||||
std::fs::write(self.path.join(LOCKFILE_FILE_NAME), string)?;
|
||||
|
@ -122,35 +156,45 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading or writing files
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when reading the manifest file
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ManifestReadError {
|
||||
/// An IO error occurred
|
||||
#[error("io error reading manifest file")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// An error occurred while deserializing the manifest file
|
||||
#[error("error deserializing manifest file")]
|
||||
Serde(#[from] toml::de::Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading the lockfile
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum LockfileReadError {
|
||||
/// An IO error occurred
|
||||
#[error("io error reading lockfile")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// An error occurred while deserializing the lockfile
|
||||
#[error("error deserializing lockfile")]
|
||||
Serde(#[from] toml::de::Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when writing the lockfile
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum LockfileWriteError {
|
||||
/// An IO error occurred
|
||||
#[error("io error writing lockfile")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// An error occurred while serializing the lockfile
|
||||
#[error("error serializing lockfile")]
|
||||
Serde(#[from] toml::ser::Error),
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ impl Visitor for TypeVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the types exported by a file
|
||||
pub fn get_file_types(file: &str) -> Result<Vec<String>, Vec<full_moon::Error>> {
|
||||
let ast = full_moon::parse(file)?;
|
||||
let mut visitor = TypeVisitor { types: vec![] };
|
||||
|
@ -49,6 +50,7 @@ pub fn get_file_types(file: &str) -> Result<Vec<String>, Vec<full_moon::Error>>
|
|||
Ok(visitor.types)
|
||||
}
|
||||
|
||||
/// Generate a linking module for a library
|
||||
pub fn generate_lib_linking_module<I: IntoIterator<Item = S>, S: AsRef<str>>(
|
||||
path: &str,
|
||||
types: I,
|
||||
|
@ -80,6 +82,7 @@ fn luau_style_path(path: &Path) -> String {
|
|||
format!("{require:?}")
|
||||
}
|
||||
|
||||
/// Get the require path for a library
|
||||
pub fn get_lib_require_path(
|
||||
target: &TargetKind,
|
||||
base_dir: &Path,
|
||||
|
@ -121,10 +124,12 @@ pub fn get_lib_require_path(
|
|||
luau_style_path(&path)
|
||||
}
|
||||
|
||||
/// Generate a linking module for a binary
|
||||
pub fn generate_bin_linking_module(path: &str) -> String {
|
||||
format!("return require({path})")
|
||||
}
|
||||
|
||||
/// Get the require path for a binary
|
||||
pub fn get_bin_require_path(
|
||||
base_dir: &Path,
|
||||
bin_file: &RelativePathBuf,
|
||||
|
|
|
@ -13,6 +13,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// Generates linking modules for a project
|
||||
pub mod generator;
|
||||
|
||||
fn create_and_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
|
||||
|
@ -28,6 +29,7 @@ fn write_cas(destination: PathBuf, cas_dir: &Path, contents: &str) -> std::io::R
|
|||
}
|
||||
|
||||
impl Project {
|
||||
/// Links the dependencies of the project
|
||||
pub fn link_dependencies(&self, graph: &DownloadedGraph) -> Result<(), errors::LinkingError> {
|
||||
let manifest = self.deser_manifest()?;
|
||||
|
||||
|
@ -207,27 +209,35 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur while linking dependencies
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur while linking dependencies
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum LinkingError {
|
||||
/// An error occurred while deserializing the project manifest
|
||||
#[error("error deserializing project manifest")]
|
||||
Manifest(#[from] crate::errors::ManifestReadError),
|
||||
|
||||
/// An error occurred while interacting with the filesystem
|
||||
#[error("error interacting with filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// A dependency was not found
|
||||
#[error("dependency not found: {0}@{1}")]
|
||||
DependencyNotFound(String, String),
|
||||
|
||||
/// The library file was not found
|
||||
#[error("library file at {0} not found")]
|
||||
LibFileNotFound(String),
|
||||
|
||||
/// An error occurred while parsing a Luau script
|
||||
#[error("error parsing Luau script at {0}")]
|
||||
FullMoon(String, Vec<full_moon::Error>),
|
||||
|
||||
/// An error occurred while generating a Roblox sync config
|
||||
#[cfg(feature = "roblox")]
|
||||
#[error("error generating roblox sync config for {0}")]
|
||||
GenerateRobloxSyncConfig(String, #[source] std::io::Error),
|
||||
|
|
|
@ -17,19 +17,26 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// A graph of dependencies
|
||||
pub type Graph<Node> = BTreeMap<PackageNames, BTreeMap<VersionId, Node>>;
|
||||
|
||||
/// A dependency graph node
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DependencyGraphNode {
|
||||
/// The alias and specifiers for the dependency, if it is a direct dependency (i.e. used by the current project)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub direct: Option<(String, DependencySpecifiers)>,
|
||||
/// The dependencies of the package
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub dependencies: BTreeMap<PackageNames, (VersionId, String)>,
|
||||
/// The type of the dependency
|
||||
pub ty: DependencyType,
|
||||
/// The package reference
|
||||
pub pkg_ref: PackageRefs,
|
||||
}
|
||||
|
||||
impl DependencyGraphNode {
|
||||
/// Returns the folder to store dependencies in for this package
|
||||
pub fn base_folder(&self, project_target: TargetKind, is_top_level: bool) -> String {
|
||||
if is_top_level || self.pkg_ref.use_new_structure() {
|
||||
project_target.packages_folder(&self.pkg_ref.target_kind())
|
||||
|
@ -38,6 +45,7 @@ impl DependencyGraphNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the folder to store the contents of the package in
|
||||
pub fn container_folder<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: &P,
|
||||
|
@ -51,8 +59,10 @@ impl DependencyGraphNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// A graph of `DependencyGraphNode`s
|
||||
pub type DependencyGraph = Graph<DependencyGraphNode>;
|
||||
|
||||
/// Inserts a node into a graph
|
||||
pub fn insert_node(
|
||||
graph: &mut DependencyGraph,
|
||||
name: PackageNames,
|
||||
|
@ -92,23 +102,33 @@ pub fn insert_node(
|
|||
}
|
||||
}
|
||||
|
||||
/// A downloaded dependency graph node, i.e. a `DependencyGraphNode` with a `Target`
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DownloadedDependencyGraphNode {
|
||||
/// The target of the package
|
||||
pub target: Target,
|
||||
/// The node
|
||||
#[serde(flatten)]
|
||||
pub node: DependencyGraphNode,
|
||||
}
|
||||
|
||||
/// A graph of `DownloadedDependencyGraphNode`s
|
||||
pub type DownloadedGraph = Graph<DownloadedDependencyGraphNode>;
|
||||
|
||||
/// A lockfile
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Lockfile {
|
||||
/// The name of the package
|
||||
pub name: PackageName,
|
||||
/// The version of the package
|
||||
pub version: Version,
|
||||
/// The target of the package
|
||||
pub target: TargetKind,
|
||||
/// The overrides of the package
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,
|
||||
|
||||
/// The graph of dependencies
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub graph: DownloadedGraph,
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ fn run() -> anyhow::Result<()> {
|
|||
parent.as_os_str() != "bin"
|
||||
|| parent
|
||||
.parent()
|
||||
.is_some_and(|parent| parent.as_os_str() != cli::HOME_DIR)
|
||||
.is_some_and(|parent| parent.as_os_str() != HOME_DIR)
|
||||
}) {
|
||||
break 'scripts;
|
||||
}
|
||||
|
|
|
@ -10,32 +10,46 @@ use crate::{
|
|||
source::specifiers::DependencySpecifiers,
|
||||
};
|
||||
|
||||
/// Overrides
|
||||
pub mod overrides;
|
||||
/// Targets
|
||||
pub mod target;
|
||||
|
||||
/// A package manifest
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Manifest {
|
||||
/// The name of the package
|
||||
pub name: PackageName,
|
||||
/// The version of the package
|
||||
pub version: Version,
|
||||
/// The description of the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
/// The license of the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub license: Option<String>,
|
||||
/// The authors of the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub authors: Option<Vec<String>>,
|
||||
/// The repository of the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub repository: Option<String>,
|
||||
/// The target of the package
|
||||
pub target: Target,
|
||||
/// Whether the package is private
|
||||
#[serde(default)]
|
||||
pub private: bool,
|
||||
/// The scripts of the package
|
||||
#[serde(default, skip_serializing)]
|
||||
pub scripts: BTreeMap<String, RelativePathBuf>,
|
||||
/// The indices to use for the package
|
||||
#[serde(
|
||||
default,
|
||||
serialize_with = "crate::util::serialize_gix_url_map",
|
||||
deserialize_with = "crate::util::deserialize_gix_url_map"
|
||||
)]
|
||||
pub indices: BTreeMap<String, gix::Url>,
|
||||
/// The indices to use for the package's wally dependencies
|
||||
#[cfg(feature = "wally-compat")]
|
||||
#[serde(
|
||||
default,
|
||||
|
@ -44,10 +58,13 @@ pub struct Manifest {
|
|||
deserialize_with = "crate::util::deserialize_gix_url_map"
|
||||
)]
|
||||
pub wally_indices: BTreeMap<String, gix::Url>,
|
||||
/// The overrides this package has
|
||||
#[serde(default, skip_serializing)]
|
||||
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,
|
||||
/// The files to include in the package
|
||||
#[serde(default)]
|
||||
pub includes: BTreeSet<String>,
|
||||
/// The patches to apply to packages
|
||||
#[cfg(feature = "patches")]
|
||||
#[serde(default, skip_serializing)]
|
||||
pub patches: BTreeMap<
|
||||
|
@ -55,25 +72,34 @@ pub struct Manifest {
|
|||
BTreeMap<crate::source::version_id::VersionId, RelativePathBuf>,
|
||||
>,
|
||||
#[serde(default, skip_serializing)]
|
||||
/// Which version of the pesde CLI this package uses
|
||||
pub pesde_version: Option<Version>,
|
||||
|
||||
/// The standard dependencies of the package
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub dependencies: BTreeMap<String, DependencySpecifiers>,
|
||||
/// The peer dependencies of the package
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub peer_dependencies: BTreeMap<String, DependencySpecifiers>,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
/// The dev dependencies of the package
|
||||
#[serde(default, skip_serializing)]
|
||||
pub dev_dependencies: BTreeMap<String, DependencySpecifiers>,
|
||||
}
|
||||
|
||||
/// A dependency type
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DependencyType {
|
||||
/// A standard dependency
|
||||
Standard,
|
||||
Dev,
|
||||
/// A peer dependency
|
||||
Peer,
|
||||
/// A dev dependency
|
||||
Dev,
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
/// Get all dependencies from the manifest
|
||||
pub fn all_dependencies(
|
||||
&self,
|
||||
) -> Result<
|
||||
|
@ -98,12 +124,15 @@ impl Manifest {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when interacting with manifests
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when trying to get all dependencies from a manifest
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum AllDependenciesError {
|
||||
/// Another specifier is already using the alias
|
||||
#[error("another specifier is already using the alias {0}")]
|
||||
AliasConflict(String),
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
/// An override key
|
||||
#[derive(
|
||||
Debug, DeserializeFromStr, SerializeDisplay, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
|
||||
)]
|
||||
|
@ -46,12 +47,15 @@ impl Display for OverrideKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when interacting with override keys
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when parsing an override key
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum OverrideKeyFromStr {
|
||||
/// The override key is empty
|
||||
#[error("empty override key")]
|
||||
Empty,
|
||||
}
|
||||
|
|
|
@ -6,13 +6,17 @@ use std::{
|
|||
str::FromStr,
|
||||
};
|
||||
|
||||
/// A kind of target
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
pub enum TargetKind {
|
||||
/// A Roblox target
|
||||
#[cfg(feature = "roblox")]
|
||||
Roblox,
|
||||
/// A Lune target
|
||||
#[cfg(feature = "lune")]
|
||||
Lune,
|
||||
/// A Luau target
|
||||
#[cfg(feature = "luau")]
|
||||
Luau,
|
||||
}
|
||||
|
@ -47,6 +51,7 @@ impl FromStr for TargetKind {
|
|||
}
|
||||
|
||||
impl TargetKind {
|
||||
/// All possible target variants
|
||||
pub const VARIANTS: &'static [TargetKind] = &[
|
||||
#[cfg(feature = "roblox")]
|
||||
TargetKind::Roblox,
|
||||
|
@ -56,7 +61,8 @@ impl TargetKind {
|
|||
TargetKind::Luau,
|
||||
];
|
||||
|
||||
// self is the project's target, dependency is the target of the dependency
|
||||
/// Whether this target is compatible with another target
|
||||
/// self is the project's target, dependency is the target of the dependency
|
||||
pub fn is_compatible_with(&self, dependency: &Self) -> bool {
|
||||
if self == dependency {
|
||||
return true;
|
||||
|
@ -70,6 +76,8 @@ impl TargetKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// The folder to store packages in for this target
|
||||
/// self is the project's target, dependency is the target of the dependency
|
||||
pub fn packages_folder(&self, dependency: &Self) -> String {
|
||||
if self == dependency {
|
||||
return "packages".to_string();
|
||||
|
@ -79,33 +87,44 @@ impl TargetKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// A target of a package
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "snake_case", tag = "environment")]
|
||||
pub enum Target {
|
||||
/// A Roblox target
|
||||
#[cfg(feature = "roblox")]
|
||||
Roblox {
|
||||
/// The path to the lib export file
|
||||
#[serde(default)]
|
||||
lib: Option<RelativePathBuf>,
|
||||
/// The files to include in the sync tool's config
|
||||
#[serde(default)]
|
||||
build_files: BTreeSet<String>,
|
||||
},
|
||||
/// A Lune target
|
||||
#[cfg(feature = "lune")]
|
||||
Lune {
|
||||
/// The path to the lib export file
|
||||
#[serde(default)]
|
||||
lib: Option<RelativePathBuf>,
|
||||
/// The path to the bin export file
|
||||
#[serde(default)]
|
||||
bin: Option<RelativePathBuf>,
|
||||
},
|
||||
/// A Luau target
|
||||
#[cfg(feature = "luau")]
|
||||
Luau {
|
||||
/// The path to the lib export file
|
||||
#[serde(default)]
|
||||
lib: Option<RelativePathBuf>,
|
||||
/// The path to the bin export file
|
||||
#[serde(default)]
|
||||
bin: Option<RelativePathBuf>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Target {
|
||||
/// Returns the kind of this target
|
||||
pub fn kind(&self) -> TargetKind {
|
||||
match self {
|
||||
#[cfg(feature = "roblox")]
|
||||
|
@ -117,6 +136,7 @@ impl Target {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the lib export file
|
||||
pub fn lib_path(&self) -> Option<&RelativePathBuf> {
|
||||
match self {
|
||||
#[cfg(feature = "roblox")]
|
||||
|
@ -128,6 +148,7 @@ impl Target {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the bin export file
|
||||
pub fn bin_path(&self) -> Option<&RelativePathBuf> {
|
||||
match self {
|
||||
#[cfg(feature = "roblox")]
|
||||
|
@ -139,6 +160,7 @@ impl Target {
|
|||
}
|
||||
}
|
||||
|
||||
/// Validates the target for publishing
|
||||
pub fn validate_publish(&self) -> Result<(), errors::TargetValidatePublishError> {
|
||||
let has_exports = match self {
|
||||
#[cfg(feature = "roblox")]
|
||||
|
@ -170,23 +192,29 @@ impl Display for Target {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when working with targets
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when validating a target for publishing
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TargetValidatePublishError {
|
||||
/// No exported files specified
|
||||
#[error("no exported files specified")]
|
||||
NoExportedFiles,
|
||||
|
||||
/// Roblox target must have at least one build file
|
||||
#[cfg(feature = "roblox")]
|
||||
#[error("roblox target must have at least one build file")]
|
||||
NoBuildFiles,
|
||||
}
|
||||
|
||||
/// Errors that can occur when parsing a target kind from a string
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TargetKindFromStr {
|
||||
/// The target kind is unknown
|
||||
#[error("unknown target kind {0}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
|
19
src/names.rs
19
src/names.rs
|
@ -2,9 +2,12 @@ use std::{fmt::Display, str::FromStr};
|
|||
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
|
||||
/// The invalid part of a package name
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorReason {
|
||||
/// The scope of the package name is invalid
|
||||
Scope,
|
||||
/// The name of the package name is invalid
|
||||
Name,
|
||||
}
|
||||
|
||||
|
@ -17,6 +20,7 @@ impl Display for ErrorReason {
|
|||
}
|
||||
}
|
||||
|
||||
/// A pesde package name
|
||||
#[derive(
|
||||
Debug, DeserializeFromStr, SerializeDisplay, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
|
||||
)]
|
||||
|
@ -59,29 +63,35 @@ impl Display for PackageName {
|
|||
}
|
||||
|
||||
impl PackageName {
|
||||
/// Returns the parts of the package name
|
||||
pub fn as_str(&self) -> (&str, &str) {
|
||||
(&self.0, &self.1)
|
||||
}
|
||||
|
||||
/// Returns the package name as a string suitable for use in the filesystem
|
||||
pub fn escaped(&self) -> String {
|
||||
format!("{}+{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// All possible package names
|
||||
#[derive(
|
||||
Debug, DeserializeFromStr, SerializeDisplay, Clone, Hash, PartialEq, Eq, PartialOrd, Ord,
|
||||
)]
|
||||
pub enum PackageNames {
|
||||
/// A pesde package name
|
||||
Pesde(PackageName),
|
||||
}
|
||||
|
||||
impl PackageNames {
|
||||
/// Returns the parts of the package name
|
||||
pub fn as_str(&self) -> (&str, &str) {
|
||||
match self {
|
||||
PackageNames::Pesde(name) => name.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the package name as a string suitable for use in the filesystem
|
||||
pub fn escaped(&self) -> String {
|
||||
match self {
|
||||
PackageNames::Pesde(name) => name.escaped(),
|
||||
|
@ -109,32 +119,41 @@ impl FromStr for PackageNames {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when working with package names
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::names::ErrorReason;
|
||||
|
||||
/// Errors that can occur when working with pesde package names
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PackageNameError {
|
||||
/// The package name is not in the format `scope/name`
|
||||
#[error("package name `{0}` is not in the format `scope/name`")]
|
||||
InvalidFormat(String),
|
||||
|
||||
/// The package name is outside the allowed characters: a-z, 0-9, and _
|
||||
#[error("package {0} `{1}` contains characters outside a-z, 0-9, and _")]
|
||||
InvalidCharacters(ErrorReason, String),
|
||||
|
||||
/// The package name contains only digits
|
||||
#[error("package {0} `{1}` contains only digits")]
|
||||
OnlyDigits(ErrorReason, String),
|
||||
|
||||
/// The package name starts or ends with an underscore
|
||||
#[error("package {0} `{1}` starts or ends with an underscore")]
|
||||
PrePostfixUnderscore(ErrorReason, String),
|
||||
|
||||
/// The package name is not within 3-32 characters long
|
||||
#[error("package {0} `{1}` is not within 3-32 characters long")]
|
||||
InvalidLength(ErrorReason, String),
|
||||
}
|
||||
|
||||
/// Errors that can occur when working with package names
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum PackageNamesError {
|
||||
/// The pesde package name is invalid
|
||||
#[error("invalid package name {0}")]
|
||||
InvalidPackageName(String),
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use git2::{ApplyLocation, ApplyOptions, Diff, DiffFormat, DiffLineType, Reposito
|
|||
use relative_path::RelativePathBuf;
|
||||
use std::{fs::read, path::Path};
|
||||
|
||||
/// Set up a git repository for patches
|
||||
pub fn setup_patches_repo<P: AsRef<Path>>(dir: P) -> Result<Repository, git2::Error> {
|
||||
let repo = Repository::init(&dir)?;
|
||||
|
||||
|
@ -31,6 +32,7 @@ pub fn setup_patches_repo<P: AsRef<Path>>(dir: P) -> Result<Repository, git2::Er
|
|||
Ok(repo)
|
||||
}
|
||||
|
||||
/// Create a patch from the current state of the repository
|
||||
pub fn create_patch<P: AsRef<Path>>(dir: P) -> Result<Vec<u8>, git2::Error> {
|
||||
let mut patches = vec![];
|
||||
let repo = Repository::open(dir.as_ref())?;
|
||||
|
@ -65,6 +67,7 @@ pub fn create_patch<P: AsRef<Path>>(dir: P) -> Result<Vec<u8>, git2::Error> {
|
|||
}
|
||||
|
||||
impl Project {
|
||||
/// Apply patches to the project's dependencies
|
||||
pub fn apply_patches(&self, graph: &DownloadedGraph) -> Result<(), errors::ApplyPatchesError> {
|
||||
let manifest = self.deser_manifest()?;
|
||||
|
||||
|
@ -140,27 +143,34 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when using patches
|
||||
pub mod errors {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{names::PackageNames, source::version_id::VersionId};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when applying patches
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ApplyPatchesError {
|
||||
/// Error deserializing the project manifest
|
||||
#[error("error deserializing project manifest")]
|
||||
ManifestDeserializationFailed(#[from] crate::errors::ManifestReadError),
|
||||
|
||||
/// Error interacting with git
|
||||
#[error("error interacting with git")]
|
||||
GitError(#[from] git2::Error),
|
||||
|
||||
/// Error reading the patch file
|
||||
#[error("error reading patch file at {0}")]
|
||||
PatchReadError(PathBuf, #[source] std::io::Error),
|
||||
|
||||
/// Error removing the .git directory
|
||||
#[error("error removing .git directory")]
|
||||
GitDirectoryRemovalError(PathBuf, #[source] std::io::Error),
|
||||
|
||||
/// Package not found in the graph
|
||||
#[error("package {0}@{1} not found in graph")]
|
||||
PackageNotFound(PackageNames, VersionId),
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
||||
impl Project {
|
||||
/// Create a dependency graph from the project's manifest
|
||||
pub fn dependency_graph(
|
||||
&self,
|
||||
previous_graph: Option<&DependencyGraph>,
|
||||
|
@ -293,27 +294,35 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when resolving dependencies
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when creating a dependency graph
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum DependencyGraphError {
|
||||
/// An error occurred while deserializing the manifest
|
||||
#[error("failed to deserialize manifest")]
|
||||
ManifestRead(#[from] crate::errors::ManifestReadError),
|
||||
|
||||
|
||||
/// An error occurred while reading all dependencies from the manifest
|
||||
#[error("error getting all project dependencies")]
|
||||
AllDependencies(#[from] crate::manifest::errors::AllDependenciesError),
|
||||
|
||||
/// An index was not found in the manifest
|
||||
#[error("index named {0} not found in manifest")]
|
||||
IndexNotFound(String),
|
||||
|
||||
/// An error occurred while refreshing a package source
|
||||
#[error("error refreshing package source")]
|
||||
Refresh(#[from] crate::source::errors::RefreshError),
|
||||
|
||||
|
||||
/// An error occurred while resolving a package
|
||||
#[error("error resolving package")]
|
||||
Resolve(#[from] crate::source::errors::ResolveError),
|
||||
|
||||
/// No matching version was found for a specifier
|
||||
#[error("no matching version found for {0}")]
|
||||
NoMatchingVersion(String),
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ use std::{
|
|||
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Script names used by pesde
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum ScriptName {
|
||||
/// Generates a config for syncing tools for Roblox. For example, for Rojo it should create a `default.project.json` file
|
||||
#[cfg(feature = "roblox")]
|
||||
RobloxSyncConfigGenerator,
|
||||
/// Prints a sourcemap for a Wally package, used for finding the library export file
|
||||
#[cfg(feature = "wally-compat")]
|
||||
SourcemapGenerator,
|
||||
}
|
||||
|
@ -27,6 +30,7 @@ impl Display for ScriptName {
|
|||
}
|
||||
}
|
||||
|
||||
/// Executes a script with the given arguments
|
||||
pub fn execute_script<A: IntoIterator<Item = S>, S: AsRef<OsStr>, P: AsRef<Path>>(
|
||||
script_name: Option<&str>,
|
||||
script_path: &Path,
|
||||
|
|
|
@ -6,14 +6,18 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// A file system entry
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FSEntry {
|
||||
/// A file with the given hash
|
||||
#[serde(rename = "f")]
|
||||
File(String),
|
||||
/// A directory
|
||||
#[serde(rename = "d")]
|
||||
Directory,
|
||||
}
|
||||
|
||||
/// A package's file system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct PackageFS(pub(crate) BTreeMap<RelativePathBuf, FSEntry>);
|
||||
|
@ -37,6 +41,7 @@ pub(crate) fn store_in_cas<P: AsRef<Path>>(
|
|||
}
|
||||
|
||||
impl PackageFS {
|
||||
/// Write the package to the given destination
|
||||
pub fn write_to<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||
&self,
|
||||
destination: P,
|
||||
|
|
|
@ -10,23 +10,32 @@ use crate::{
|
|||
Project,
|
||||
};
|
||||
|
||||
/// Packages' filesystems
|
||||
pub mod fs;
|
||||
/// The pesde package source
|
||||
pub mod pesde;
|
||||
/// Package references
|
||||
pub mod refs;
|
||||
/// Dependency specifiers
|
||||
pub mod specifiers;
|
||||
/// Traits for sources and packages
|
||||
pub mod traits;
|
||||
/// Version IDs
|
||||
pub mod version_id;
|
||||
|
||||
/// The result of resolving a package
|
||||
pub type ResolveResult<Ref> = (PackageNames, BTreeMap<VersionId, Ref>);
|
||||
|
||||
/// All possible package sources
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||
pub enum PackageSources {
|
||||
/// A pesde package source
|
||||
Pesde(pesde::PesdePackageSource),
|
||||
}
|
||||
|
||||
impl PackageSource for PackageSources {
|
||||
type Ref = PackageRefs;
|
||||
type Specifier = DependencySpecifiers;
|
||||
type Ref = PackageRefs;
|
||||
type RefreshError = errors::RefreshError;
|
||||
type ResolveError = errors::ResolveError;
|
||||
type DownloadError = errors::DownloadError;
|
||||
|
@ -77,32 +86,41 @@ impl PackageSource for PackageSources {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when interacting with a package source
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that occur when refreshing a package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum RefreshError {
|
||||
/// The pesde package source failed to refresh
|
||||
#[error("error refreshing pesde package source")]
|
||||
Pesde(#[from] crate::source::pesde::errors::RefreshError),
|
||||
}
|
||||
|
||||
/// Errors that can occur when resolving a package
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ResolveError {
|
||||
/// The dependency specifier does not match the source (if using the CLI, this is a bug - file an issue)
|
||||
#[error("mismatched dependency specifier for source")]
|
||||
Mismatch,
|
||||
|
||||
/// The pesde package source failed to resolve
|
||||
#[error("error resolving pesde package")]
|
||||
Pesde(#[from] crate::source::pesde::errors::ResolveError),
|
||||
}
|
||||
|
||||
/// Errors that can occur when downloading a package
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum DownloadError {
|
||||
/// The package ref does not match the source (if using the CLI, this is a bug - file an issue)
|
||||
#[error("mismatched package ref for source")]
|
||||
Mismatch,
|
||||
|
||||
/// The pesde package source failed to download
|
||||
#[error("error downloading pesde package")]
|
||||
Pesde(#[from] crate::source::pesde::errors::DownloadError),
|
||||
}
|
||||
|
|
|
@ -25,22 +25,29 @@ use crate::{
|
|||
Project,
|
||||
};
|
||||
|
||||
/// The pesde package reference
|
||||
pub mod pkg_ref;
|
||||
/// The pesde dependency specifier
|
||||
pub mod specifier;
|
||||
|
||||
/// The pesde package source
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||
pub struct PesdePackageSource {
|
||||
repo_url: gix::Url,
|
||||
}
|
||||
|
||||
/// The file containing scope information
|
||||
pub const SCOPE_INFO_FILE: &str = "scope.toml";
|
||||
|
||||
/// Information about a scope
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ScopeInfo {
|
||||
/// The people authorized to publish packages to this scope
|
||||
pub owners: BTreeSet<u64>,
|
||||
}
|
||||
|
||||
impl PesdePackageSource {
|
||||
/// Creates a new pesde package source
|
||||
pub fn new(repo_url: gix::Url) -> Self {
|
||||
Self { repo_url }
|
||||
}
|
||||
|
@ -49,10 +56,12 @@ impl PesdePackageSource {
|
|||
self.repo_url.to_bstring().to_vec()
|
||||
}
|
||||
|
||||
/// The path to the index
|
||||
pub fn path(&self, project: &Project) -> std::path::PathBuf {
|
||||
project.data_dir.join("indices").join(hash(self.as_bytes()))
|
||||
}
|
||||
|
||||
/// The URL of the repository
|
||||
pub fn repo_url(&self) -> &gix::Url {
|
||||
&self.repo_url
|
||||
}
|
||||
|
@ -108,6 +117,7 @@ impl PesdePackageSource {
|
|||
}
|
||||
}
|
||||
|
||||
/// Reads a file from the index
|
||||
pub fn read_file<
|
||||
I: IntoIterator<Item = P> + Clone,
|
||||
P: ToString + PartialEq<gix::bstr::BStr>,
|
||||
|
@ -154,6 +164,7 @@ impl PesdePackageSource {
|
|||
Ok(Some(string))
|
||||
}
|
||||
|
||||
/// Reads the config file
|
||||
pub fn config(&self, project: &Project) -> Result<IndexConfig, errors::ConfigError> {
|
||||
let file = self.read_file(["config.toml"], project).map_err(Box::new)?;
|
||||
|
||||
|
@ -171,6 +182,7 @@ impl PesdePackageSource {
|
|||
Ok(config)
|
||||
}
|
||||
|
||||
/// Reads all packages from the index
|
||||
pub fn all_packages(
|
||||
&self,
|
||||
project: &Project,
|
||||
|
@ -253,6 +265,7 @@ impl PesdePackageSource {
|
|||
Ok(packages)
|
||||
}
|
||||
|
||||
/// The git2 repository for the index
|
||||
#[cfg(feature = "git2")]
|
||||
pub fn repo_git2(&self, project: &Project) -> Result<git2::Repository, git2::Error> {
|
||||
let path = self.path(project);
|
||||
|
@ -262,8 +275,8 @@ impl PesdePackageSource {
|
|||
}
|
||||
|
||||
impl PackageSource for PesdePackageSource {
|
||||
type Ref = PesdePackageRef;
|
||||
type Specifier = PesdeDependencySpecifier;
|
||||
type Ref = PesdePackageRef;
|
||||
type RefreshError = errors::RefreshError;
|
||||
type ResolveError = errors::ResolveError;
|
||||
type DownloadError = errors::DownloadError;
|
||||
|
@ -446,23 +459,31 @@ impl PackageSource for PesdePackageSource {
|
|||
}
|
||||
}
|
||||
|
||||
/// The configuration for the pesde index
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct IndexConfig {
|
||||
/// The URL of the API
|
||||
pub api: url::Url,
|
||||
/// The URL to download packages from
|
||||
pub download: Option<String>,
|
||||
/// Whether git is allowed as a source for publishing packages
|
||||
#[serde(default)]
|
||||
pub git_allowed: bool,
|
||||
/// Whether other registries are allowed as a source for publishing packages
|
||||
#[serde(default)]
|
||||
pub other_registries_allowed: bool,
|
||||
/// The OAuth client ID for GitHub
|
||||
pub github_oauth_client_id: String,
|
||||
}
|
||||
|
||||
impl IndexConfig {
|
||||
/// The URL of the API
|
||||
pub fn api(&self) -> &str {
|
||||
self.api.as_str().trim_end_matches('/')
|
||||
}
|
||||
|
||||
/// The URL to download packages from
|
||||
pub fn download(&self) -> String {
|
||||
self.download
|
||||
.as_deref()
|
||||
|
@ -471,181 +492,239 @@ impl IndexConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// The entry in a package's index file
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct IndexFileEntry {
|
||||
/// The target for this package
|
||||
pub target: Target,
|
||||
/// When this package was published
|
||||
#[serde(default = "chrono::Utc::now")]
|
||||
pub published_at: chrono::DateTime<chrono::Utc>,
|
||||
|
||||
/// The description of this package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
/// The license of this package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub license: Option<String>,
|
||||
|
||||
/// The dependencies of this package
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
|
||||
}
|
||||
|
||||
/// The index file for a package
|
||||
pub type IndexFile = BTreeMap<VersionId, IndexFileEntry>;
|
||||
|
||||
/// Errors that can occur when interacting with the pesde package source
|
||||
pub mod errors {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when refreshing the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum RefreshError {
|
||||
/// Error interacting with the filesystem
|
||||
#[error("error interacting with the filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// Error opening the repository
|
||||
#[error("error opening repository at {0}")]
|
||||
Open(PathBuf, #[source] gix::open::Error),
|
||||
|
||||
|
||||
/// No default remote found in repository
|
||||
#[error("no default remote found in repository at {0}")]
|
||||
NoDefaultRemote(PathBuf),
|
||||
|
||||
/// Error getting default remote from repository
|
||||
#[error("error getting default remote from repository at {0}")]
|
||||
GetDefaultRemote(PathBuf, #[source] gix::remote::find::existing::Error),
|
||||
|
||||
/// Error connecting to remote repository
|
||||
#[error("error connecting to remote repository at {0}")]
|
||||
Connect(gix::Url, #[source] gix::remote::connect::Error),
|
||||
|
||||
/// Error preparing fetch from remote repository
|
||||
#[error("error preparing fetch from remote repository at {0}")]
|
||||
PrepareFetch(gix::Url, #[source] gix::remote::fetch::prepare::Error),
|
||||
|
||||
/// Error reading from remote repository
|
||||
#[error("error reading from remote repository at {0}")]
|
||||
Read(gix::Url, #[source] gix::remote::fetch::Error),
|
||||
|
||||
/// Error cloning repository
|
||||
#[error("error cloning repository from {0}")]
|
||||
Clone(gix::Url, #[source] gix::clone::Error),
|
||||
|
||||
/// Error fetching repository
|
||||
#[error("error fetching repository from {0}")]
|
||||
Fetch(gix::Url, #[source] gix::clone::fetch::Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading the pesde package source's tree
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum TreeError {
|
||||
/// Error interacting with the filesystem
|
||||
#[error("error interacting with the filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// No default remote found in repository
|
||||
#[error("no default remote found in repository at {0}")]
|
||||
NoDefaultRemote(PathBuf),
|
||||
|
||||
/// Error getting default remote from repository
|
||||
#[error("error getting default remote from repository at {0}")]
|
||||
GetDefaultRemote(PathBuf, #[source] Box<gix::remote::find::existing::Error>),
|
||||
|
||||
/// Error getting refspec from remote repository
|
||||
#[error("no refspecs found in repository at {0}")]
|
||||
NoRefSpecs(PathBuf),
|
||||
|
||||
/// Error getting local refspec from remote repository
|
||||
#[error("no local refspec found in repository at {0}")]
|
||||
NoLocalRefSpec(PathBuf),
|
||||
|
||||
/// Error finding reference in repository
|
||||
#[error("no reference found for local refspec {0}")]
|
||||
NoReference(String, #[source] gix::reference::find::existing::Error),
|
||||
|
||||
/// Error peeling reference in repository
|
||||
#[error("cannot peel reference {0}")]
|
||||
CannotPeel(String, #[source] gix::reference::peel::Error),
|
||||
|
||||
/// Error converting id to object in repository
|
||||
#[error("error converting id {0} to object")]
|
||||
CannotConvertToObject(String, #[source] gix::object::find::existing::Error),
|
||||
|
||||
/// Error peeling object to tree in repository
|
||||
#[error("error peeling object {0} to tree")]
|
||||
CannotPeelToTree(String, #[source] gix::object::peel::to_kind::Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading a file from the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ReadFile {
|
||||
/// Error opening the repository
|
||||
#[error("error opening repository at {0}")]
|
||||
Open(PathBuf, #[source] Box<gix::open::Error>),
|
||||
|
||||
/// Error reading tree from repository
|
||||
#[error("error getting tree from repository at {0}")]
|
||||
Tree(PathBuf, #[source] Box<TreeError>),
|
||||
|
||||
/// Error looking up entry in tree
|
||||
#[error("error looking up entry {0} in tree")]
|
||||
Lookup(String, #[source] gix::object::find::existing::Error),
|
||||
|
||||
/// Error reading file as utf8
|
||||
#[error("error parsing file for {0} as utf8")]
|
||||
Utf8(String, #[source] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when resolving a package from the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ResolveError {
|
||||
/// Error interacting with the filesystem
|
||||
#[error("error interacting with the filesystem")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
/// Package not found in index
|
||||
#[error("package {0} not found")]
|
||||
NotFound(String),
|
||||
|
||||
/// Error reading file for package
|
||||
#[error("error reading file for {0}")]
|
||||
Read(String, #[source] Box<ReadFile>),
|
||||
|
||||
/// Error parsing file for package
|
||||
#[error("error parsing file for {0}")]
|
||||
Parse(String, #[source] toml::de::Error),
|
||||
|
||||
/// Error parsing file for package as utf8
|
||||
#[error("error parsing file for {0} to utf8")]
|
||||
Utf8(String, #[source] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading the config file for the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ConfigError {
|
||||
/// Error reading file
|
||||
#[error("error reading config file")]
|
||||
ReadFile(#[from] Box<ReadFile>),
|
||||
|
||||
/// Error parsing config file
|
||||
#[error("error parsing config file")]
|
||||
Parse(#[from] toml::de::Error),
|
||||
|
||||
/// The config file is missing
|
||||
#[error("missing config file for index at {0}")]
|
||||
Missing(Box<gix::Url>),
|
||||
}
|
||||
|
||||
/// Errors that can occur when reading all packages from the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum AllPackagesError {
|
||||
/// Error opening the repository
|
||||
#[error("error opening repository at {0}")]
|
||||
Open(PathBuf, #[source] Box<gix::open::Error>),
|
||||
|
||||
/// Error reading tree from repository
|
||||
#[error("error getting tree from repository at {0}")]
|
||||
Tree(PathBuf, #[source] Box<TreeError>),
|
||||
|
||||
/// Error decoding entry in repository
|
||||
#[error("error decoding entry in repository at {0}")]
|
||||
Decode(PathBuf, #[source] gix::objs::decode::Error),
|
||||
|
||||
/// Error converting entry in repository
|
||||
#[error("error converting entry in repository at {0}")]
|
||||
Convert(PathBuf, #[source] gix::object::find::existing::Error),
|
||||
|
||||
/// Error deserializing file in repository
|
||||
#[error("error deserializing file {0} in repository at {1}")]
|
||||
Deserialize(String, PathBuf, #[source] Box<toml::de::Error>),
|
||||
|
||||
/// Error parsing file in repository as utf8
|
||||
#[error("error parsing file for {0} as utf8")]
|
||||
Utf8(String, #[source] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
/// Errors that can occur when downloading a package from the pesde package source
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum DownloadError {
|
||||
/// Error reading index file
|
||||
#[error("error reading config file")]
|
||||
ReadFile(#[from] Box<ConfigError>),
|
||||
|
||||
/// Error downloading package
|
||||
#[error("error downloading package")]
|
||||
Download(#[from] reqwest::Error),
|
||||
|
||||
/// Error unpacking package
|
||||
#[error("error unpacking package")]
|
||||
Unpack(#[from] std::io::Error),
|
||||
|
||||
/// Error writing index file
|
||||
#[error("error writing index file")]
|
||||
WriteIndex(#[source] std::io::Error),
|
||||
|
||||
/// Error serializing index file
|
||||
#[error("error serializing index file")]
|
||||
SerializeIndex(#[from] toml::ser::Error),
|
||||
|
||||
/// Error deserializing index file
|
||||
#[error("error deserializing index file")]
|
||||
DeserializeIndex(#[from] toml::de::Error),
|
||||
|
||||
/// Error writing index file
|
||||
#[error("error reading index file")]
|
||||
ReadIndex(#[source] std::io::Error),
|
||||
}
|
||||
|
|
|
@ -3,12 +3,17 @@ use semver::VersionReq;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
/// The specifier for a pesde dependency
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PesdeDependencySpecifier {
|
||||
/// The name of the package
|
||||
pub name: PackageName,
|
||||
/// The version requirement for the package
|
||||
pub version: VersionReq,
|
||||
/// The index to use for the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub index: Option<String>,
|
||||
/// The target to use for the package
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub target: Option<TargetKind>,
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ use crate::{
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// All possible package references
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "snake_case", tag = "ref_ty")]
|
||||
pub enum PackageRefs {
|
||||
/// A pesde package reference
|
||||
Pesde(pesde::pkg_ref::PesdePackageRef),
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@ use crate::source::{pesde, traits::DependencySpecifier};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
/// All possible dependency specifiers
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
#[serde(untagged)]
|
||||
pub enum DependencySpecifiers {
|
||||
/// A pesde dependency specifier
|
||||
Pesde(pesde::specifier::PesdeDependencySpecifier),
|
||||
}
|
||||
impl DependencySpecifier for DependencySpecifiers {}
|
||||
|
|
|
@ -12,26 +12,40 @@ use crate::{
|
|||
Project,
|
||||
};
|
||||
|
||||
/// A specifier for a dependency
|
||||
pub trait DependencySpecifier: Debug + Display {}
|
||||
|
||||
/// A reference to a package
|
||||
pub trait PackageRef: Debug {
|
||||
/// The dependencies of this package
|
||||
fn dependencies(&self) -> &BTreeMap<String, (DependencySpecifiers, DependencyType)>;
|
||||
/// Whether to use the new structure (`packages` folders inside the package's content folder) or the old structure (Wally-style, with linker files in the parent of the folder containing the package's contents)
|
||||
fn use_new_structure(&self) -> bool;
|
||||
/// The target of this package
|
||||
fn target_kind(&self) -> TargetKind;
|
||||
/// The source of this package
|
||||
fn source(&self) -> PackageSources;
|
||||
}
|
||||
|
||||
/// A source of packages
|
||||
pub trait PackageSource: Debug {
|
||||
type Ref: PackageRef;
|
||||
/// The specifier type for this source
|
||||
type Specifier: DependencySpecifier;
|
||||
/// The reference type for this source
|
||||
type Ref: PackageRef;
|
||||
/// The error type for refreshing this source
|
||||
type RefreshError: std::error::Error;
|
||||
/// The error type for resolving a package from this source
|
||||
type ResolveError: std::error::Error;
|
||||
/// The error type for downloading a package from this source
|
||||
type DownloadError: std::error::Error;
|
||||
|
||||
/// Refreshes the source
|
||||
fn refresh(&self, _project: &Project) -> Result<(), Self::RefreshError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolves a specifier to a reference
|
||||
fn resolve(
|
||||
&self,
|
||||
specifier: &Self::Specifier,
|
||||
|
@ -39,6 +53,7 @@ pub trait PackageSource: Debug {
|
|||
project_target: TargetKind,
|
||||
) -> Result<ResolveResult<Self::Ref>, Self::ResolveError>;
|
||||
|
||||
/// Downloads a package
|
||||
fn download(
|
||||
&self,
|
||||
pkg_ref: &Self::Ref,
|
||||
|
|
|
@ -3,24 +3,29 @@ use semver::Version;
|
|||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
/// A version ID, which is a combination of a version and a target
|
||||
#[derive(
|
||||
Debug, SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
|
||||
)]
|
||||
pub struct VersionId(pub(crate) Version, pub(crate) TargetKind);
|
||||
|
||||
impl VersionId {
|
||||
/// Creates a new version ID
|
||||
pub fn new(version: Version, target: TargetKind) -> Self {
|
||||
VersionId(version, target)
|
||||
}
|
||||
|
||||
/// Access the version
|
||||
pub fn version(&self) -> &Version {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Access the target
|
||||
pub fn target(&self) -> &TargetKind {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Returns this version ID as a string that can be used in the filesystem
|
||||
pub fn escaped(&self) -> String {
|
||||
format!("{}+{}", self.0, self.1)
|
||||
}
|
||||
|
@ -47,18 +52,23 @@ impl FromStr for VersionId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when using a version ID
|
||||
pub mod errors {
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors that can occur when parsing a version ID
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum VersionIdParseError {
|
||||
#[error("malformed entry key {0}")]
|
||||
/// The version ID is malformed
|
||||
#[error("malformed version id {0}")]
|
||||
Malformed(String),
|
||||
|
||||
/// The version is malformed
|
||||
#[error("malformed version")]
|
||||
Version(#[from] semver::Error),
|
||||
|
||||
/// The target is malformed
|
||||
#[error("malformed target")]
|
||||
Target(#[from] crate::manifest::target::errors::TargetKindFromStr),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue