use crate::ser_display_deser_fromstr;
use relative_path::{RelativePath, RelativePathBuf};
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
fmt::{Display, Formatter},
str::FromStr,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(test, derive(schemars::JsonSchema))]
#[cfg_attr(test, schemars(rename_all = "snake_case"))]
pub enum TargetKind {
Roblox,
RobloxServer,
Lune,
Luau,
}
ser_display_deser_fromstr!(TargetKind);
impl Display for TargetKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TargetKind::Roblox => write!(f, "roblox"),
TargetKind::RobloxServer => write!(f, "roblox_server"),
TargetKind::Lune => write!(f, "lune"),
TargetKind::Luau => write!(f, "luau"),
}
}
}
impl FromStr for TargetKind {
type Err = errors::TargetKindFromStr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"roblox" => Ok(Self::Roblox),
"roblox_server" => Ok(Self::RobloxServer),
"lune" => Ok(Self::Lune),
"luau" => Ok(Self::Luau),
t => Err(errors::TargetKindFromStr::Unknown(t.to_string())),
}
}
}
impl TargetKind {
pub const VARIANTS: &'static [TargetKind] = &[
TargetKind::Roblox,
TargetKind::RobloxServer,
TargetKind::Lune,
TargetKind::Luau,
];
#[must_use]
pub fn packages_folder(self, dependency: Self) -> String {
format!("{dependency}_packages")
}
#[must_use]
pub fn is_roblox(self) -> bool {
matches!(self, TargetKind::Roblox | TargetKind::RobloxServer)
}
#[must_use]
pub fn has_bin(self) -> bool {
!self.is_roblox()
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(test, derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case", tag = "environment")]
pub enum Target {
Roblox {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
lib: Option<RelativePathBuf>,
#[serde(default)]
build_files: BTreeSet<String>,
},
RobloxServer {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
lib: Option<RelativePathBuf>,
#[serde(default)]
build_files: BTreeSet<String>,
},
Lune {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
lib: Option<RelativePathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
bin: Option<RelativePathBuf>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[cfg_attr(test, schemars(with = "BTreeMap<String, std::path::PathBuf>"))]
scripts: BTreeMap<String, RelativePathBuf>,
},
Luau {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
lib: Option<RelativePathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[cfg_attr(test, schemars(with = "Option<std::path::PathBuf>"))]
bin: Option<RelativePathBuf>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
#[cfg_attr(test, schemars(with = "BTreeMap<String, std::path::PathBuf>"))]
scripts: BTreeMap<String, RelativePathBuf>,
},
}
impl Target {
#[must_use]
pub fn kind(&self) -> TargetKind {
match self {
Target::Roblox { .. } => TargetKind::Roblox,
Target::RobloxServer { .. } => TargetKind::RobloxServer,
Target::Lune { .. } => TargetKind::Lune,
Target::Luau { .. } => TargetKind::Luau,
}
}
#[must_use]
pub fn lib_path(&self) -> Option<&RelativePath> {
match self {
Target::Roblox { lib, .. } => lib.as_deref(),
Target::RobloxServer { lib, .. } => lib.as_deref(),
Target::Lune { lib, .. } => lib.as_deref(),
Target::Luau { lib, .. } => lib.as_deref(),
}
}
#[must_use]
pub fn bin_path(&self) -> Option<&RelativePath> {
match self {
Target::Roblox { .. } => None,
Target::RobloxServer { .. } => None,
Target::Lune { bin, .. } => bin.as_deref(),
Target::Luau { bin, .. } => bin.as_deref(),
}
}
#[must_use]
pub fn build_files(&self) -> Option<&BTreeSet<String>> {
match self {
Target::Roblox { build_files, .. } => Some(build_files),
Target::RobloxServer { build_files, .. } => Some(build_files),
_ => None,
}
}
#[must_use]
pub fn scripts(&self) -> Option<&BTreeMap<String, RelativePathBuf>> {
match self {
Target::Lune { scripts, .. } => Some(scripts),
Target::Luau { scripts, .. } => Some(scripts),
_ => None,
}
}
}
impl Display for Target {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.kind())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[cfg_attr(test, derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum RobloxPlaceKind {
Shared,
Server,
}
impl TryInto<RobloxPlaceKind> for TargetKind {
type Error = ();
fn try_into(self) -> Result<RobloxPlaceKind, Self::Error> {
match self {
TargetKind::Roblox => Ok(RobloxPlaceKind::Shared),
TargetKind::RobloxServer => Ok(RobloxPlaceKind::Server),
_ => Err(()),
}
}
}
impl Display for RobloxPlaceKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RobloxPlaceKind::Shared => write!(f, "shared"),
RobloxPlaceKind::Server => write!(f, "server"),
}
}
}
pub mod errors {
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum TargetKindFromStr {
#[error("unknown target kind {0}")]
Unknown(String),
}
}