feat: add roblox server target

This commit is contained in:
daimond113 2024-09-06 23:38:44 +02:00
parent 30c4d0c391
commit 10c804e2f3
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
10 changed files with 232 additions and 75 deletions

View file

@ -2,7 +2,10 @@ use crate::{error::Error, storage::StorageImpl};
use actix_web::{http::header::LOCATION, HttpResponse}; use actix_web::{http::header::LOCATION, HttpResponse};
use pesde::{names::PackageName, source::version_id::VersionId}; use pesde::{names::PackageName, source::version_id::VersionId};
use reqwest::header::{CONTENT_ENCODING, CONTENT_TYPE}; use reqwest::header::{CONTENT_ENCODING, CONTENT_TYPE};
use rusty_s3::{actions::PutObject, Bucket, Credentials, S3Action}; use rusty_s3::{
actions::{GetObject, PutObject},
Bucket, Credentials, S3Action,
};
use std::{fmt::Display, time::Duration}; use std::{fmt::Display, time::Duration};
#[derive(Debug)] #[derive(Debug)]
@ -48,7 +51,7 @@ impl StorageImpl for S3Storage {
package_name: &PackageName, package_name: &PackageName,
version: &VersionId, version: &VersionId,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let object_url = PutObject::new( let object_url = GetObject::new(
&self.s3_bucket, &self.s3_bucket,
Some(&self.s3_credentials), Some(&self.s3_credentials),
&format!( &format!(
@ -97,7 +100,7 @@ impl StorageImpl for S3Storage {
package_name: &PackageName, package_name: &PackageName,
version: &VersionId, version: &VersionId,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let object_url = PutObject::new( let object_url = GetObject::new(
&self.s3_bucket, &self.s3_bucket,
Some(&self.s3_credentials), Some(&self.s3_credentials),
&format!( &format!(
@ -133,7 +136,7 @@ impl StorageImpl for S3Storage {
} }
async fn get_doc(&self, doc_hash: &str) -> Result<HttpResponse, Error> { async fn get_doc(&self, doc_hash: &str) -> Result<HttpResponse, Error> {
let object_url = PutObject::new( let object_url = GetObject::new(
&self.s3_bucket, &self.s3_bucket,
Some(&self.s3_credentials), Some(&self.s3_credentials),
&format!("doc/{}.gz", doc_hash), &format!("doc/{}.gz", doc_hash),

View file

@ -1,17 +1,17 @@
use std::{
io::{Seek, Write},
path::Component,
};
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use colored::Colorize; use colored::Colorize;
use reqwest::StatusCode; use reqwest::StatusCode;
use semver::VersionReq; use semver::VersionReq;
use std::{
io::{Seek, Write},
path::Component,
};
use tempfile::tempfile; use tempfile::tempfile;
use crate::cli::up_to_date_lockfile;
use pesde::{ use pesde::{
manifest::target::Target, manifest::{target::Target, DependencyType},
scripts::ScriptName, scripts::ScriptName,
source::{ source::{
pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource}, pesde::{specifier::PesdeDependencySpecifier, PesdePackageSource},
@ -42,10 +42,39 @@ impl PublishCommand {
return Ok(()); return Ok(());
} }
manifest if manifest.target.lib_path().is_none() && manifest.target.bin_path().is_none() {
.target anyhow::bail!("no exports found in target");
.validate_publish() }
.context("manifest not fit for publishing")?;
#[cfg(feature = "roblox")]
if matches!(
manifest.target,
Target::Roblox { .. } | Target::RobloxServer { .. }
) {
if !manifest.target.build_files().is_some_and(|f| !f.is_empty()) {
anyhow::bail!("no build files found in target");
}
match up_to_date_lockfile(&project)? {
Some(lockfile) => {
if lockfile
.graph
.values()
.flatten()
.filter_map(|(_, node)| node.node.direct.as_ref().map(|_| node))
.any(|node| {
node.target.build_files().is_none()
&& !matches!(node.node.ty, DependencyType::Dev)
})
{
anyhow::bail!("roblox packages may not depend on non-roblox packages");
}
}
None => {
anyhow::bail!("outdated lockfile, please run the install command first")
}
}
}
let mut archive = tar::Builder::new(flate2::write::GzEncoder::new( let mut archive = tar::Builder::new(flate2::write::GzEncoder::new(
vec![], vec![],
@ -65,6 +94,7 @@ impl PublishCommand {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
let mut roblox_target = match &mut manifest.target { let mut roblox_target = match &mut manifest.target {
Target::Roblox { build_files, .. } => Some(build_files), Target::Roblox { build_files, .. } => Some(build_files),
Target::RobloxServer { build_files, .. } => Some(build_files),
_ => None, _ => None,
}; };
#[cfg(not(feature = "roblox"))] #[cfg(not(feature = "roblox"))]

View file

@ -1,6 +1,6 @@
use std::path::{Component, Path}; use std::path::{Component, Path};
use crate::manifest::target::TargetKind; use crate::manifest::{target::TargetKind, Manifest};
use full_moon::{ast::luau::ExportedTypeDeclaration, visitors::Visitor}; use full_moon::{ast::luau::ExportedTypeDeclaration, visitors::Visitor};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
@ -82,14 +82,20 @@ fn luau_style_path(path: &Path) -> String {
format!("{require:?}") format!("{require:?}")
} }
// This function should be simplified (especially to reduce the number of arguments),
// but it's not clear how to do that while maintaining the current functionality.
/// Get the require path for a library /// Get the require path for a library
#[allow(clippy::too_many_arguments)]
pub fn get_lib_require_path( pub fn get_lib_require_path(
target: &TargetKind, target: &TargetKind,
base_dir: &Path, base_dir: &Path,
lib_file: &RelativePathBuf, lib_file: &RelativePathBuf,
destination_dir: &Path, destination_dir: &Path,
use_new_structure: bool, use_new_structure: bool,
) -> String { root_container_dir: &Path,
container_dir: &Path,
project_manifest: &Manifest,
) -> Result<String, errors::GetLibRequirePath> {
let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap(); let path = pathdiff::diff_paths(destination_dir, base_dir).unwrap();
let path = if use_new_structure { let path = if use_new_structure {
log::debug!("using new structure for require path with {:?}", lib_file); log::debug!("using new structure for require path with {:?}", lib_file);
@ -100,7 +106,25 @@ pub fn get_lib_require_path(
}; };
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
if matches!(target, TargetKind::Roblox) { if matches!(target, TargetKind::Roblox | TargetKind::RobloxServer) {
let (prefix, path) = match target.try_into() {
Ok(place_kind) if !destination_dir.starts_with(root_container_dir) => (
project_manifest
.place
.get(&place_kind)
.ok_or(errors::GetLibRequirePath::RobloxPlaceKindPathNotFound(
place_kind,
))?
.as_str(),
if use_new_structure {
lib_file.to_path(container_dir)
} else {
container_dir.to_path_buf()
},
),
_ => ("script.Parent", path),
};
let path = path let path = path
.components() .components()
.filter_map(|component| match component { .filter_map(|component| match component {
@ -118,10 +142,10 @@ pub fn get_lib_require_path(
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(""); .join("");
return format!("script.Parent{path}"); return Ok(format!("{prefix}{path}"));
}; };
luau_style_path(&path) Ok(luau_style_path(&path))
} }
/// Generate a linking module for a binary /// Generate a linking module for a binary
@ -144,3 +168,17 @@ pub fn get_bin_require_path(
luau_style_path(&path) luau_style_path(&path)
} }
/// Errors for the linking module utilities
pub mod errors {
use thiserror::Error;
/// An error occurred while getting the require path for a library
#[derive(Debug, Error)]
pub enum GetLibRequirePath {
/// The path for the RobloxPlaceKind could not be found
#[cfg(feature = "roblox")]
#[error("could not find the path for the RobloxPlaceKind {0}")]
RobloxPlaceKindPathNotFound(crate::manifest::target::RobloxPlaceKind),
}
}

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
linking::generator::get_file_types, linking::generator::get_file_types,
lockfile::DownloadedGraph, lockfile::DownloadedGraph,
manifest::target::Target,
names::PackageNames, names::PackageNames,
scripts::{execute_script, ScriptName}, scripts::{execute_script, ScriptName},
source::{fs::store_in_cas, traits::PackageRef, version_id::VersionId}, source::{fs::store_in_cas, traits::PackageRef, version_id::VersionId},
@ -92,8 +91,9 @@ impl Project {
.insert(version_id, types); .insert(version_id, types);
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
if let Some(Target::Roblox { build_files, .. }) = if let Some(build_files) = Some(&node.target)
Some(&node.target).filter(|_| !node.node.pkg_ref.like_wally()) .filter(|_| !node.node.pkg_ref.like_wally())
.and_then(|t| t.build_files())
{ {
let script_name = ScriptName::RobloxSyncConfigGenerator.to_string(); let script_name = ScriptName::RobloxSyncConfigGenerator.to_string();
@ -122,7 +122,7 @@ impl Project {
for (name, versions) in graph { for (name, versions) in graph {
for (version_id, node) in versions { for (version_id, node) in versions {
let node_container_folder = { let (node_container_folder, node_packages_folder) = {
let base_folder = create_and_canonicalize( let base_folder = create_and_canonicalize(
self.package_dir().join( self.package_dir().join(
manifest manifest
@ -158,7 +158,10 @@ impl Project {
lib_file, lib_file,
&container_folder, &container_folder,
node.node.pkg_ref.use_new_structure(), node.node.pkg_ref.use_new_structure(),
), &base_folder,
container_folder.strip_prefix(&base_folder).unwrap(),
&manifest,
)?,
types, types,
), ),
)?; )?;
@ -180,7 +183,7 @@ impl Project {
} }
} }
container_folder (container_folder, base_folder)
}; };
for (dependency_name, (dependency_version_id, dependency_alias)) in for (dependency_name, (dependency_version_id, dependency_alias)) in
@ -200,15 +203,21 @@ impl Project {
continue; continue;
}; };
let packages_container_folder = create_and_canonicalize( let base_folder = create_and_canonicalize(
self.package_dir().join( self.package_dir().join(
node.node node.node
.pkg_ref .pkg_ref
.target_kind() .target_kind()
.packages_folder(&dependency_node.node.pkg_ref.target_kind()), .packages_folder(&dependency_node.node.pkg_ref.target_kind()),
), ),
)? )?;
.join(PACKAGES_CONTAINER_NAME); let packages_container_folder = base_folder.join(PACKAGES_CONTAINER_NAME);
let container_folder = dependency_node.node.container_folder(
&packages_container_folder,
dependency_name,
dependency_version_id.version(),
);
let linker_folder = create_and_canonicalize( let linker_folder = create_and_canonicalize(
node_container_folder node_container_folder
@ -223,13 +232,12 @@ impl Project {
&dependency_node.target.kind(), &dependency_node.target.kind(),
&linker_folder, &linker_folder,
lib_file, lib_file,
&dependency_node.node.container_folder( &container_folder,
&packages_container_folder,
dependency_name,
dependency_version_id.version(),
),
dependency_node.node.pkg_ref.use_new_structure(), dependency_node.node.pkg_ref.use_new_structure(),
), &node_packages_folder,
container_folder.strip_prefix(&base_folder).unwrap(),
&manifest,
)?,
package_types package_types
.get(dependency_name) .get(dependency_name)
.and_then(|v| v.get(dependency_version_id)) .and_then(|v| v.get(dependency_version_id))
@ -276,5 +284,9 @@ pub mod errors {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
#[error("error generating roblox sync config for {0}")] #[error("error generating roblox sync config for {0}")]
GenerateRobloxSyncConfig(String, #[source] std::io::Error), GenerateRobloxSyncConfig(String, #[source] std::io::Error),
/// An error occurred while getting the require path for a library
#[error("error getting require path for library")]
GetLibRequirePath(#[from] super::generator::errors::GetLibRequirePath),
} }
} }

View file

@ -77,6 +77,10 @@ pub struct Manifest {
/// A list of globs pointing to workspace members' directories /// A list of globs pointing to workspace members' directories
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub workspace_members: Vec<String>, pub workspace_members: Vec<String>,
#[cfg(feature = "roblox")]
/// The Roblox place of this project
#[serde(default, skip_serializing)]
pub place: BTreeMap<target::RobloxPlaceKind, String>,
/// The standard dependencies of the package /// The standard dependencies of the package
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]

View file

@ -1,5 +1,6 @@
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::{ use std::{
collections::BTreeSet, collections::BTreeSet,
fmt::{Display, Formatter}, fmt::{Display, Formatter},
@ -7,12 +8,16 @@ use std::{
}; };
/// A kind of target /// A kind of target
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(
#[serde(rename_all = "snake_case", deny_unknown_fields)] SerializeDisplay, DeserializeFromStr, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub enum TargetKind { pub enum TargetKind {
/// A Roblox target /// A Roblox target
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
Roblox, Roblox,
/// A Roblox server target
#[cfg(feature = "roblox")]
RobloxServer,
/// A Lune target /// A Lune target
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
Lune, Lune,
@ -26,6 +31,8 @@ impl Display for TargetKind {
match self { match self {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
TargetKind::Roblox => write!(f, "roblox"), TargetKind::Roblox => write!(f, "roblox"),
#[cfg(feature = "roblox")]
TargetKind::RobloxServer => write!(f, "roblox_server"),
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
TargetKind::Lune => write!(f, "lune"), TargetKind::Lune => write!(f, "lune"),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -41,6 +48,8 @@ impl FromStr for TargetKind {
match s { match s {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
"roblox" => Ok(Self::Roblox), "roblox" => Ok(Self::Roblox),
#[cfg(feature = "roblox")]
"roblox_server" => Ok(Self::RobloxServer),
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
"lune" => Ok(Self::Lune), "lune" => Ok(Self::Lune),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -55,6 +64,8 @@ impl TargetKind {
pub const VARIANTS: &'static [TargetKind] = &[ pub const VARIANTS: &'static [TargetKind] = &[
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
TargetKind::Roblox, TargetKind::Roblox,
#[cfg(feature = "roblox")]
TargetKind::RobloxServer,
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
TargetKind::Lune, TargetKind::Lune,
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -72,6 +83,9 @@ impl TargetKind {
#[cfg(all(feature = "lune", feature = "luau"))] #[cfg(all(feature = "lune", feature = "luau"))]
(TargetKind::Lune, TargetKind::Luau) => true, (TargetKind::Lune, TargetKind::Luau) => true,
#[cfg(feature = "roblox")]
(TargetKind::RobloxServer, TargetKind::Roblox) => true,
_ => false, _ => false,
} }
} }
@ -101,6 +115,16 @@ pub enum Target {
#[serde(default)] #[serde(default)]
build_files: BTreeSet<String>, build_files: BTreeSet<String>,
}, },
/// A Roblox server target
#[cfg(feature = "roblox")]
RobloxServer {
/// 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 /// A Lune target
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
Lune { Lune {
@ -129,6 +153,8 @@ impl Target {
match self { match self {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
Target::Roblox { .. } => TargetKind::Roblox, Target::Roblox { .. } => TargetKind::Roblox,
#[cfg(feature = "roblox")]
Target::RobloxServer { .. } => TargetKind::RobloxServer,
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
Target::Lune { .. } => TargetKind::Lune, Target::Lune { .. } => TargetKind::Lune,
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -141,6 +167,8 @@ impl Target {
match self { match self {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
Target::Roblox { lib, .. } => lib.as_ref(), Target::Roblox { lib, .. } => lib.as_ref(),
#[cfg(feature = "roblox")]
Target::RobloxServer { lib, .. } => lib.as_ref(),
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
Target::Lune { lib, .. } => lib.as_ref(), Target::Lune { lib, .. } => lib.as_ref(),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -153,6 +181,8 @@ impl Target {
match self { match self {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
Target::Roblox { .. } => None, Target::Roblox { .. } => None,
#[cfg(feature = "roblox")]
Target::RobloxServer { .. } => None,
#[cfg(feature = "lune")] #[cfg(feature = "lune")]
Target::Lune { bin, .. } => bin.as_ref(), Target::Lune { bin, .. } => bin.as_ref(),
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
@ -160,28 +190,14 @@ impl Target {
} }
} }
/// Validates the target for publishing /// Returns the Roblox build files
pub fn validate_publish(&self) -> Result<(), errors::TargetValidatePublishError> { pub fn build_files(&self) -> Option<&BTreeSet<String>> {
let has_exports = match self {
#[cfg(feature = "roblox")]
Target::Roblox { lib, .. } => lib.is_some(),
#[cfg(feature = "lune")]
Target::Lune { lib, bin } => lib.is_some() || bin.is_some(),
#[cfg(feature = "luau")]
Target::Luau { lib, bin } => lib.is_some() || bin.is_some(),
};
if !has_exports {
return Err(errors::TargetValidatePublishError::NoExportedFiles);
}
match self { match self {
#[cfg(feature = "roblox")] #[cfg(feature = "roblox")]
Target::Roblox { build_files, .. } if build_files.is_empty() => { Target::Roblox { build_files, .. } => Some(build_files),
Err(errors::TargetValidatePublishError::NoBuildFiles) #[cfg(feature = "roblox")]
} Target::RobloxServer { build_files, .. } => Some(build_files),
_ => None,
_ => Ok(()),
} }
} }
} }
@ -192,24 +208,46 @@ impl Display for Target {
} }
} }
#[cfg(feature = "roblox")]
/// The kind of a Roblox place property
#[derive(
SerializeDisplay, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd,
)]
#[serde(rename_all = "snake_case")]
pub enum RobloxPlaceKind {
/// The shared dependencies location
Shared,
/// The server dependencies location
Server,
}
#[cfg(feature = "roblox")]
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(()),
}
}
}
#[cfg(feature = "roblox")]
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"),
}
}
}
/// Errors that can occur when working with targets /// Errors that can occur when working with targets
pub mod errors { pub mod errors {
use thiserror::Error; 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 /// Errors that can occur when parsing a target kind from a string
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[non_exhaustive] #[non_exhaustive]

View file

@ -255,6 +255,10 @@ impl Project {
already_resolved.ty = ty; already_resolved.ty = ty;
} }
if already_resolved.direct.is_none() && depth == 0 {
already_resolved.direct = Some((alias.clone(), specifier.clone()));
}
continue; continue;
} }

View file

@ -257,7 +257,11 @@ impl PackageSource for GitPackageSource {
#[cfg(feature = "wally-compat")] #[cfg(feature = "wally-compat")]
None => { None => {
match self match self
.read_file(["wally.toml"], project, Some(tree.clone())) .read_file(
[crate::source::wally::compat_util::WALLY_MANIFEST_FILE_NAME],
project,
Some(tree.clone()),
)
.map_err(|e| { .map_err(|e| {
errors::ResolveError::ReadManifest(Box::new(self.repo_url.clone()), e) errors::ResolveError::ReadManifest(Box::new(self.repo_url.clone()), e)
})? { })? {

View file

@ -7,6 +7,7 @@ use tempfile::TempDir;
use crate::{ use crate::{
manifest::target::Target, manifest::target::Target,
scripts::{execute_script, ScriptName}, scripts::{execute_script, ScriptName},
source::wally::manifest::{Realm, WallyManifest},
Project, LINK_LIB_NO_FILE_FOUND, Project, LINK_LIB_NO_FILE_FOUND,
}; };
@ -50,14 +51,24 @@ pub(crate) fn find_lib_path(
} }
} }
pub(crate) const WALLY_MANIFEST_FILE_NAME: &str = "wally.toml";
pub(crate) fn get_target( pub(crate) fn get_target(
project: &Project, project: &Project,
tempdir: &TempDir, tempdir: &TempDir,
) -> Result<Target, errors::FindLibPathError> { ) -> Result<Target, errors::FindLibPathError> {
Ok(Target::Roblox { let lib = find_lib_path(project, tempdir.path())?
lib: find_lib_path(project, tempdir.path())? .or_else(|| Some(RelativePathBuf::from(LINK_LIB_NO_FILE_FOUND)));
.or_else(|| Some(RelativePathBuf::from(LINK_LIB_NO_FILE_FOUND))), let build_files = Default::default();
build_files: Default::default(),
let manifest = tempdir.path().join(WALLY_MANIFEST_FILE_NAME);
let manifest = std::fs::read_to_string(&manifest)?;
let manifest: WallyManifest = toml::from_str(&manifest)?;
Ok(if matches!(manifest.package.realm, Realm::Shared) {
Target::Roblox { lib, build_files }
} else {
Target::RobloxServer { lib, build_files }
}) })
} }
@ -79,5 +90,9 @@ pub mod errors {
/// An error occurred while deserializing the sourcemap result /// An error occurred while deserializing the sourcemap result
#[error("error deserializing sourcemap result")] #[error("error deserializing sourcemap result")]
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
/// An error occurred while deserializing the wally manifest
#[error("error deserializing wally manifest")]
WallyManifest(#[from] toml::de::Error),
} }
} }

View file

@ -9,12 +9,21 @@ use crate::{
source::{specifiers::DependencySpecifiers, wally::specifier::WallyDependencySpecifier}, source::{specifiers::DependencySpecifiers, wally::specifier::WallyDependencySpecifier},
}; };
#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum Realm {
#[serde(alias = "dev")]
Shared,
Server,
}
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub struct WallyPackage { pub struct WallyPackage {
pub name: WallyPackageName, pub name: WallyPackageName,
pub version: Version, pub version: Version,
pub registry: url::Url, pub registry: url::Url,
pub realm: Realm,
} }
pub fn deserialize_specifiers<'de, D: Deserializer<'de>>( pub fn deserialize_specifiers<'de, D: Deserializer<'de>>(