2024-07-23 15:37:47 +01:00
|
|
|
use crate::{
|
|
|
|
names::{PackageName, PackageNames},
|
|
|
|
source::{DependencySpecifiers, VersionId},
|
|
|
|
};
|
2024-03-04 20:18:49 +00:00
|
|
|
use relative_path::RelativePathBuf;
|
2024-03-24 13:31:11 +00:00
|
|
|
use semver::Version;
|
2024-07-22 15:41:45 +01:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-07-12 23:09:37 +01:00
|
|
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
|
|
|
use std::{
|
2024-07-22 15:41:45 +01:00
|
|
|
collections::{BTreeMap, BTreeSet},
|
2024-07-12 23:09:37 +01:00
|
|
|
fmt::{Display, Formatter},
|
|
|
|
str::FromStr,
|
2024-03-24 13:31:11 +00:00
|
|
|
};
|
2024-03-04 20:18:49 +00:00
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
2024-03-04 20:18:49 +00:00
|
|
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
2024-07-14 14:19:15 +01:00
|
|
|
pub enum TargetKind {
|
2024-07-12 23:09:37 +01:00
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
Roblox,
|
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
Lune,
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
Luau,
|
2024-03-04 20:18:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-14 14:19:15 +01:00
|
|
|
impl Display for TargetKind {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
2024-03-04 20:18:49 +00:00
|
|
|
match self {
|
2024-07-12 23:09:37 +01:00
|
|
|
#[cfg(feature = "roblox")]
|
2024-07-14 14:19:15 +01:00
|
|
|
TargetKind::Roblox => write!(f, "roblox"),
|
2024-07-12 23:09:37 +01:00
|
|
|
#[cfg(feature = "lune")]
|
2024-07-14 14:19:15 +01:00
|
|
|
TargetKind::Lune => write!(f, "lune"),
|
2024-07-12 23:09:37 +01:00
|
|
|
#[cfg(feature = "luau")]
|
2024-07-14 14:19:15 +01:00
|
|
|
TargetKind::Luau => write!(f, "luau"),
|
2024-03-04 20:18:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-22 19:12:56 +01:00
|
|
|
impl FromStr for TargetKind {
|
|
|
|
type Err = errors::TargetKindFromStr;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
"roblox" => Ok(Self::Roblox),
|
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
"lune" => Ok(Self::Lune),
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
"luau" => Ok(Self::Luau),
|
|
|
|
t => Err(errors::TargetKindFromStr::Unknown(t.to_string())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-14 14:19:15 +01:00
|
|
|
impl TargetKind {
|
2024-07-23 23:53:34 +01:00
|
|
|
pub const VARIANTS: &'static [TargetKind] = &[
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
TargetKind::Roblox,
|
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
TargetKind::Lune,
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
TargetKind::Luau,
|
|
|
|
];
|
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
// self is the project's target, dependency is the target of the dependency
|
2024-07-14 14:19:15 +01:00
|
|
|
pub fn is_compatible_with(&self, dependency: &Self) -> bool {
|
2024-07-12 23:09:37 +01:00
|
|
|
if self == dependency {
|
|
|
|
return true;
|
2024-03-04 20:18:49 +00:00
|
|
|
}
|
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
match (self, dependency) {
|
|
|
|
#[cfg(all(feature = "lune", feature = "luau"))]
|
2024-07-14 14:19:15 +01:00
|
|
|
(TargetKind::Lune, TargetKind::Luau) => true,
|
2024-07-12 23:09:37 +01:00
|
|
|
|
|
|
|
_ => false,
|
2024-03-04 20:18:49 +00:00
|
|
|
}
|
|
|
|
}
|
2024-07-14 14:19:15 +01:00
|
|
|
|
|
|
|
pub fn packages_folder(&self, dependency: &Self) -> String {
|
|
|
|
if self == dependency {
|
|
|
|
return "packages".to_string();
|
|
|
|
}
|
|
|
|
|
2024-07-22 15:41:45 +01:00
|
|
|
format!("{dependency}_packages")
|
2024-07-14 14:19:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-17 18:38:01 +01:00
|
|
|
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(rename_all = "snake_case", tag = "environment")]
|
2024-07-14 14:19:15 +01:00
|
|
|
pub enum Target {
|
|
|
|
#[cfg(feature = "roblox")]
|
2024-07-22 15:41:45 +01:00
|
|
|
Roblox {
|
|
|
|
#[serde(default)]
|
|
|
|
lib: Option<RelativePathBuf>,
|
|
|
|
#[serde(default)]
|
|
|
|
build_files: BTreeSet<String>,
|
|
|
|
},
|
2024-07-14 14:19:15 +01:00
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
Lune {
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default)]
|
2024-07-14 14:19:15 +01:00
|
|
|
lib: Option<RelativePathBuf>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default)]
|
2024-07-14 14:19:15 +01:00
|
|
|
bin: Option<RelativePathBuf>,
|
|
|
|
},
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
Luau {
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default)]
|
2024-07-14 14:19:15 +01:00
|
|
|
lib: Option<RelativePathBuf>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default)]
|
2024-07-14 14:19:15 +01:00
|
|
|
bin: Option<RelativePathBuf>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-07-17 18:38:01 +01:00
|
|
|
impl Target {
|
|
|
|
pub fn kind(&self) -> TargetKind {
|
|
|
|
match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
Target::Roblox { .. } => TargetKind::Roblox,
|
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
Target::Lune { .. } => TargetKind::Lune,
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
Target::Luau { .. } => TargetKind::Luau,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lib_path(&self) -> Option<&RelativePathBuf> {
|
|
|
|
match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
2024-07-22 15:41:45 +01:00
|
|
|
Target::Roblox { lib, .. } => lib.as_ref(),
|
2024-07-17 18:38:01 +01:00
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
Target::Lune { lib, .. } => lib.as_ref(),
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
Target::Luau { lib, .. } => lib.as_ref(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-22 15:41:45 +01:00
|
|
|
pub fn bin_path(&self) -> Option<&RelativePathBuf> {
|
|
|
|
match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
Target::Roblox { .. } => None,
|
|
|
|
#[cfg(feature = "lune")]
|
|
|
|
Target::Lune { bin, .. } => bin.as_ref(),
|
|
|
|
#[cfg(feature = "luau")]
|
|
|
|
Target::Luau { bin, .. } => bin.as_ref(),
|
|
|
|
}
|
2024-07-17 18:38:01 +01:00
|
|
|
}
|
2024-07-14 14:19:15 +01:00
|
|
|
|
2024-07-22 15:41:45 +01:00
|
|
|
pub fn validate_publish(&self) -> Result<(), errors::TargetValidatePublishError> {
|
|
|
|
let has_exports = match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
Target::Roblox { lib, .. } => lib.is_some(),
|
2024-07-14 14:19:15 +01:00
|
|
|
#[cfg(feature = "lune")]
|
2024-07-22 15:41:45 +01:00
|
|
|
Target::Lune { lib, bin } => lib.is_some() || bin.is_some(),
|
2024-07-14 14:19:15 +01:00
|
|
|
#[cfg(feature = "luau")]
|
2024-07-22 15:41:45 +01:00
|
|
|
Target::Luau { lib, bin } => lib.is_some() || bin.is_some(),
|
2024-07-14 14:19:15 +01:00
|
|
|
};
|
|
|
|
|
2024-07-22 15:41:45 +01:00
|
|
|
if !has_exports {
|
|
|
|
return Err(errors::TargetValidatePublishError::NoExportedFiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
Target::Roblox { build_files, .. } if build_files.is_empty() => {
|
|
|
|
Err(errors::TargetValidatePublishError::NoBuildFiles)
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => Ok(()),
|
|
|
|
}
|
2024-07-14 14:19:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Target {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.kind())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
#[derive(
|
|
|
|
Debug, DeserializeFromStr, SerializeDisplay, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
|
|
|
|
)]
|
|
|
|
pub struct OverrideKey(pub Vec<Vec<String>>);
|
2024-03-10 18:03:12 +00:00
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
impl FromStr for OverrideKey {
|
|
|
|
type Err = errors::OverrideKeyFromStr;
|
2024-03-10 18:03:12 +00:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2024-07-14 14:19:15 +01:00
|
|
|
let overrides = s
|
|
|
|
.split(',')
|
|
|
|
.map(|overrides| overrides.split('>').map(|s| s.to_string()).collect())
|
|
|
|
.collect::<Vec<Vec<String>>>();
|
|
|
|
|
|
|
|
if overrides.is_empty() {
|
|
|
|
return Err(errors::OverrideKeyFromStr::Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Self(overrides))
|
2024-03-10 18:03:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
impl Display for OverrideKey {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}",
|
|
|
|
self.0
|
2024-03-26 22:39:58 +00:00
|
|
|
.iter()
|
|
|
|
.map(|overrides| {
|
|
|
|
overrides
|
|
|
|
.iter()
|
2024-07-12 23:09:37 +01:00
|
|
|
.map(|o| o.as_str())
|
2024-03-26 22:39:58 +00:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(">")
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
2024-07-12 23:09:37 +01:00
|
|
|
.join(",")
|
2024-03-26 22:39:58 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-22 15:41:45 +01:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
|
|
pub enum ScriptName {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
RobloxSyncConfigGenerator,
|
2024-07-23 23:53:34 +01:00
|
|
|
#[cfg(feature = "wally-compat")]
|
2024-07-22 15:41:45 +01:00
|
|
|
SourcemapGenerator,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for ScriptName {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
ScriptName::RobloxSyncConfigGenerator => write!(f, "roblox_sync_config_generator"),
|
2024-07-23 23:53:34 +01:00
|
|
|
#[cfg(feature = "wally-compat")]
|
2024-07-22 15:41:45 +01:00
|
|
|
ScriptName::SourcemapGenerator => write!(f, "sourcemap_generator"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
2024-03-04 20:18:49 +00:00
|
|
|
pub struct Manifest {
|
2024-07-12 23:09:37 +01:00
|
|
|
pub name: PackageName,
|
2024-03-04 20:18:49 +00:00
|
|
|
pub version: Version,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
2024-03-25 16:29:31 +00:00
|
|
|
pub description: Option<String>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
2024-03-25 16:29:31 +00:00
|
|
|
pub license: Option<String>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
2024-03-25 16:29:31 +00:00
|
|
|
pub authors: Option<Vec<String>>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
2024-03-25 16:29:31 +00:00
|
|
|
pub repository: Option<String>,
|
2024-07-12 23:09:37 +01:00
|
|
|
pub target: Target,
|
2024-03-04 20:18:49 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub private: bool,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing)]
|
2024-07-14 14:19:15 +01:00
|
|
|
pub scripts: BTreeMap<String, RelativePathBuf>,
|
|
|
|
#[serde(default)]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub indices: BTreeMap<String, url::Url>,
|
2024-07-17 18:38:01 +01:00
|
|
|
#[cfg(feature = "wally-compat")]
|
2024-07-22 18:40:30 +01:00
|
|
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub wally_indices: BTreeMap<String, url::Url>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing)]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub overrides: BTreeMap<OverrideKey, DependencySpecifiers>,
|
2024-07-14 14:19:15 +01:00
|
|
|
#[serde(default)]
|
2024-07-22 15:41:45 +01:00
|
|
|
pub includes: BTreeSet<String>,
|
2024-07-23 23:53:34 +01:00
|
|
|
#[cfg(feature = "patches")]
|
2024-07-23 15:37:47 +01:00
|
|
|
#[serde(default, skip_serializing)]
|
|
|
|
pub patches: BTreeMap<PackageNames, BTreeMap<VersionId, RelativePathBuf>>,
|
2024-07-22 15:41:45 +01:00
|
|
|
|
|
|
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub dependencies: BTreeMap<String, DependencySpecifiers>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub peer_dependencies: BTreeMap<String, DependencySpecifiers>,
|
2024-07-22 15:41:45 +01:00
|
|
|
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
2024-07-12 23:09:37 +01:00
|
|
|
pub dev_dependencies: BTreeMap<String, DependencySpecifiers>,
|
2024-03-31 13:23:08 +01:00
|
|
|
}
|
|
|
|
|
2024-07-14 14:19:15 +01:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
pub enum DependencyType {
|
|
|
|
Standard,
|
|
|
|
Dev,
|
|
|
|
Peer,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Manifest {
|
|
|
|
pub fn all_dependencies(
|
|
|
|
&self,
|
|
|
|
) -> Result<
|
|
|
|
BTreeMap<String, (DependencySpecifiers, DependencyType)>,
|
|
|
|
errors::AllDependenciesError,
|
|
|
|
> {
|
|
|
|
let mut all_deps = BTreeMap::new();
|
|
|
|
|
|
|
|
for (deps, ty) in [
|
|
|
|
(&self.dependencies, DependencyType::Standard),
|
|
|
|
(&self.peer_dependencies, DependencyType::Peer),
|
|
|
|
(&self.dev_dependencies, DependencyType::Dev),
|
|
|
|
] {
|
|
|
|
for (alias, spec) in deps {
|
|
|
|
if all_deps.insert(alias.clone(), (spec.clone(), ty)).is_some() {
|
|
|
|
return Err(errors::AllDependenciesError::AliasConflict(alias.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(all_deps)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
pub mod errors {
|
|
|
|
use thiserror::Error;
|
2024-03-04 20:18:49 +00:00
|
|
|
|
2024-07-12 23:09:37 +01:00
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[non_exhaustive]
|
2024-07-14 14:19:15 +01:00
|
|
|
pub enum OverrideKeyFromStr {
|
|
|
|
#[error("empty override key")]
|
|
|
|
Empty,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum AllDependenciesError {
|
|
|
|
#[error("another specifier is already using the alias {0}")]
|
|
|
|
AliasConflict(String),
|
|
|
|
}
|
2024-07-22 15:41:45 +01:00
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum TargetValidatePublishError {
|
|
|
|
#[error("no exported files specified")]
|
|
|
|
NoExportedFiles,
|
|
|
|
|
|
|
|
#[cfg(feature = "roblox")]
|
|
|
|
#[error("roblox target must have at least one build file")]
|
|
|
|
NoBuildFiles,
|
|
|
|
}
|
2024-07-22 19:12:56 +01:00
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[non_exhaustive]
|
|
|
|
pub enum TargetKindFromStr {
|
|
|
|
#[error("unknown target kind {0}")]
|
|
|
|
Unknown(String),
|
|
|
|
}
|
2024-03-04 20:18:49 +00:00
|
|
|
}
|