libwally/manifest.rs
1use std::collections::BTreeMap;
2use std::path::Path;
3
4use anyhow::Context;
5use semver::Version;
6use serde::{Deserialize, Serialize};
7
8use crate::package_id::PackageId;
9use crate::package_name::PackageName;
10use crate::package_req::PackageReq;
11
12pub const MANIFEST_FILE_NAME: &str = "wally.toml";
13
14/// The contents of a `wally.toml` file, which defines a package.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[serde(rename_all = "kebab-case")]
17pub struct Manifest {
18 pub package: Package,
19
20 #[serde(default)]
21 pub place: PlaceInfo,
22
23 #[serde(default)]
24 pub dependencies: BTreeMap<String, PackageReq>,
25
26 #[serde(default)]
27 pub server_dependencies: BTreeMap<String, PackageReq>,
28
29 #[serde(default)]
30 pub dev_dependencies: BTreeMap<String, PackageReq>,
31}
32
33impl Manifest {
34 /// Load a manifest from a project directory containing a `wally.toml` file.
35 pub fn load(dir: &Path) -> anyhow::Result<Self> {
36 let file_path = dir.join(MANIFEST_FILE_NAME);
37
38 let content = fs_err::read_to_string(&file_path)?;
39 let manifest: Manifest = toml::from_str(&content)
40 .with_context(|| format!("failed to parse manifest at path {}", file_path.display()))?;
41
42 Ok(manifest)
43 }
44
45 pub fn from_slice(slice: &[u8]) -> anyhow::Result<Self> {
46 let manifest: Manifest =
47 toml::from_slice(slice).with_context(|| format!("failed to parse manifest"))?;
48
49 Ok(manifest)
50 }
51
52 pub fn package_id(&self) -> PackageId {
53 PackageId::new(self.package.name.clone(), self.package.version.clone())
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct Package {
59 /// The scope and name of the package.
60 ///
61 /// Example: `lpghatguy/asink`.
62 pub name: PackageName,
63
64 /// The current version of the package.
65 ///
66 /// Example: `1.0.0`
67 pub version: Version,
68
69 /// The registry that this package should pull its dependencies from.
70 ///
71 /// Example: `https://github.com/UpliftGames/wally-test-index`
72 pub registry: String,
73
74 /// The realms (`shared`, `server`, etc) that this package can be used in.
75 ///
76 /// Packages in the `shared` realm can only depend on other `shared`
77 /// packages. Packages in the `server` realm can depend on any other
78 /// package.
79 ///
80 /// Example: `shared`, `server`
81 pub realm: Realm,
82
83 /// A short description of the package.
84 ///
85 /// Example: `A game about adopting things.`
86 pub description: Option<String>,
87
88 /// An SPDX license specifier for the package.
89 ///
90 /// Example: `MIT OR Apache-2.0`
91 pub license: Option<String>,
92
93 /// A list of the package's authors.
94 ///
95 /// Example: ["Biff Lumfer <biff@playadopt.me>"]
96 #[serde(default)]
97 pub authors: Vec<String>,
98
99 /// A list of paths to include in the package. Glob patterns are supported.
100 ///
101 /// By default all directories and files are included except files generated
102 /// by wally and hidden files/directories. If include is specified then only
103 /// files matching patterns in the include list will be included.
104 ///
105 /// If include is unspecified and a .gitignore file exists then those patterns
106 /// will be respected and wally will also ignore those files.
107 ///
108 /// Example: ["/src", "*.lua"]
109 #[serde(default)]
110 pub include: Vec<String>,
111
112 /// A list of paths to exclude from the package. Glob patterns are supported.
113 ///
114 /// By default files generated by wally and hidden files/directories will be
115 /// excluded. If a .gitignore file exists and include is unspecified then
116 /// those patterns will be respected and wally will also ignore those files.
117 /// Patterns in exclude will be excluded in addition to those patterns in the
118 /// .gitignore.
119 ///
120 /// Example: ["/Packages", "/node_modules"]
121 #[serde(default)]
122 pub exclude: Vec<String>,
123
124 /// Indicates whether the package can be published or not.
125 ///
126 /// Example: true
127 #[serde(default)]
128 pub private: bool,
129}
130
131// Metadata we require when this manifest will be used to generate package folders
132// This information can be present in any package but is only used in the root package
133#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(rename_all = "kebab-case")]
135pub struct PlaceInfo {
136 /// Where the shared packages folder is located in the Roblox Datamodel
137 ///
138 /// Example: `game.ReplicatedStorage.Packages`
139 #[serde(default)]
140 pub shared_packages: Option<String>,
141
142 /// Where the server packages folder is located in the Roblox Datamodel
143 ///
144 /// Example: `game.ServerScriptStorage.Packages`
145 #[serde(default)]
146 pub server_packages: Option<String>,
147}
148
149impl Default for PlaceInfo {
150 fn default() -> Self {
151 Self {
152 shared_packages: None,
153 server_packages: None,
154 }
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
159#[serde(rename_all = "lowercase")]
160pub enum Realm {
161 Server,
162 Shared,
163 Dev,
164}
165
166impl Realm {
167 pub fn is_dependency_valid(dep_type: Self, dep_realm: Self) -> bool {
168 use Realm::*;
169
170 matches!(
171 (dep_type, dep_realm),
172 (Server, _) | (Shared, Shared) | (Dev, _)
173 )
174 }
175}