1mod build;
2mod dependency;
3mod deploy;
4mod partial;
5mod platform;
6mod rock_source;
7mod serde_util;
8mod test_spec;
9
10use std::{
11 collections::HashMap, convert::Infallible, fmt::Display, io, path::PathBuf, str::FromStr,
12};
13
14use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, UserData, Value};
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16
17pub use build::*;
18pub use dependency::*;
19pub use deploy::*;
20pub use partial::*;
21pub use platform::*;
22pub use rock_source::*;
23pub use serde_util::*;
24use ssri::Integrity;
25pub use test_spec::*;
26use thiserror::Error;
27use url::Url;
28
29use crate::{
30 config::{LuaVersion, LuaVersionUnset},
31 hash::HasIntegrity,
32 package::{PackageName, PackageSpec, PackageVersion, PackageVersionReq},
33 project::project_toml::ProjectTomlError,
34 project::ProjectRoot,
35 rockspec::{lua_dependency::LuaDependencySpec, Rockspec},
36};
37
38#[derive(Error, Debug)]
39pub enum LuaRockspecError {
40 #[error("could not parse rockspec: {0}")]
41 MLua(#[from] mlua::Error),
42 #[error("{}copy_directories cannot contain the rockspec name", ._0.as_ref().map(|p| format!("{p}: ")).unwrap_or_default())]
43 CopyDirectoriesContainRockspecName(Option<String>),
44 #[error("could not parse rockspec: {0}")]
45 LuaTable(#[from] LuaTableError),
46 #[error("cannot create Lua rockspec with off-spec dependency: {0}")]
47 OffSpecDependency(PackageName),
48 #[error("cannot create Lua rockspec with off-spec build dependency: {0}")]
49 OffSpecBuildDependency(PackageName),
50 #[error("cannot create Lua rockspec with off-spec test dependency: {0}")]
51 OffSpecTestDependency(PackageName),
52 #[error(transparent)]
53 ProjectToml(#[from] ProjectTomlError),
54}
55
56#[derive(Clone, Debug)]
57#[cfg_attr(test, derive(PartialEq))]
58pub struct LocalLuaRockspec {
59 rockspec_format: Option<RockspecFormat>,
61 package: PackageName,
63 version: PackageVersion,
65 description: RockDescription,
66 supported_platforms: PlatformSupport,
67 lua: PackageVersionReq,
69 dependencies: PerPlatform<Vec<LuaDependencySpec>>,
70 build_dependencies: PerPlatform<Vec<LuaDependencySpec>>,
71 external_dependencies: PerPlatform<HashMap<String, ExternalDependencySpec>>,
72 test_dependencies: PerPlatform<Vec<LuaDependencySpec>>,
73 build: PerPlatform<BuildSpec>,
74 source: PerPlatform<RemoteRockSource>,
75 test: PerPlatform<TestSpec>,
76 deploy: PerPlatform<DeploySpec>,
77 raw_content: String,
79}
80
81impl UserData for LocalLuaRockspec {
82 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
83 methods.add_method("package", |_, this, _: ()| Ok(this.package.clone()));
84 methods.add_method("version", |_, this, _: ()| Ok(this.version.clone()));
85 methods.add_method("description", |_, this, _: ()| Ok(this.description.clone()));
86 methods.add_method("supported_platforms", |_, this, _: ()| {
87 Ok(this.supported_platforms.clone())
88 });
89 methods.add_method("lua", |_, this, _: ()| Ok(this.lua.clone()));
90 methods.add_method("dependencies", |_, this, _: ()| {
91 Ok(this.dependencies.clone())
92 });
93 methods.add_method("build_dependencies", |_, this, _: ()| {
94 Ok(this.build_dependencies.clone())
95 });
96 methods.add_method("external_dependencies", |_, this, _: ()| {
97 Ok(this.external_dependencies.clone())
98 });
99 methods.add_method("test_dependencies", |_, this, _: ()| {
100 Ok(this.test_dependencies.clone())
101 });
102 methods.add_method("build", |_, this, _: ()| Ok(this.build.clone()));
103 methods.add_method("source", |_, this, _: ()| Ok(this.source.clone()));
104 methods.add_method("test", |_, this, _: ()| Ok(this.test.clone()));
105 methods.add_method("format", |_, this, _: ()| Ok(this.rockspec_format.clone()));
106
107 methods.add_method("to_lua_rockspec_string", |_, this, _: ()| {
108 this.to_lua_remote_rockspec_string()
109 .map_err(|err| mlua::Error::RuntimeError(err.to_string()))
110 });
111 }
112}
113
114impl LocalLuaRockspec {
115 pub fn new(
116 rockspec_content: &str,
117 project_root: ProjectRoot,
118 ) -> Result<Self, LuaRockspecError> {
119 let lua = Lua::new();
120 lua.load(rockspec_content).exec()?;
121
122 let globals = lua.globals();
123
124 let dependencies: PerPlatform<Vec<LuaDependencySpec>> = globals.get("dependencies")?;
125
126 let lua_version_req = dependencies
127 .current_platform()
128 .iter()
129 .find(|dep| dep.name().to_string() == "lua")
130 .cloned()
131 .map(|dep| dep.version_req().clone())
132 .unwrap_or(PackageVersionReq::Any);
133
134 fn strip_lua(
135 dependencies: PerPlatform<Vec<LuaDependencySpec>>,
136 ) -> PerPlatform<Vec<LuaDependencySpec>> {
137 dependencies.map(|deps| {
138 deps.iter()
139 .filter(|dep| dep.name().to_string() != "lua")
140 .cloned()
141 .collect()
142 })
143 }
144
145 let build_dependencies: PerPlatform<Vec<LuaDependencySpec>> =
146 globals.get("build_dependencies")?;
147
148 let test_dependencies: PerPlatform<Vec<LuaDependencySpec>> =
149 globals.get("test_dependencies")?;
150
151 let rockspec = LocalLuaRockspec {
152 rockspec_format: globals.get("rockspec_format")?,
153 package: globals.get("package")?,
154 version: globals.get("version")?,
155 description: parse_lua_tbl_or_default(&lua, "description")?,
156 supported_platforms: parse_lua_tbl_or_default(&lua, "supported_platforms")?,
157 lua: lua_version_req,
158 dependencies: strip_lua(dependencies),
159 build_dependencies: strip_lua(build_dependencies),
160 test_dependencies: strip_lua(test_dependencies),
161 external_dependencies: globals.get("external_dependencies")?,
162 build: globals.get("build")?,
163 test: globals.get("test")?,
164 deploy: globals.get("deploy")?,
165 raw_content: rockspec_content.into(),
166
167 source: globals
168 .get::<Option<PerPlatform<RemoteRockSource>>>("source")?
169 .unwrap_or_else(|| {
170 PerPlatform::new(RockSourceSpec::File(project_root.to_path_buf()).into())
171 }),
172 };
173
174 let rockspec_file_name = format!("{}-{}.rockspec", rockspec.package(), rockspec.version());
175 if rockspec
176 .build()
177 .default
178 .copy_directories
179 .contains(&PathBuf::from(&rockspec_file_name))
180 {
181 return Err(LuaRockspecError::CopyDirectoriesContainRockspecName(None));
182 }
183
184 for (platform, build_override) in &rockspec.build().per_platform {
185 if build_override
186 .copy_directories
187 .contains(&PathBuf::from(&rockspec_file_name))
188 {
189 return Err(LuaRockspecError::CopyDirectoriesContainRockspecName(Some(
190 platform.to_string(),
191 )));
192 }
193 }
194 Ok(rockspec)
195 }
196}
197
198impl Rockspec for LocalLuaRockspec {
199 type Error = Infallible;
200
201 fn package(&self) -> &PackageName {
202 &self.package
203 }
204
205 fn version(&self) -> &PackageVersion {
206 &self.version
207 }
208
209 fn description(&self) -> &RockDescription {
210 &self.description
211 }
212
213 fn supported_platforms(&self) -> &PlatformSupport {
214 &self.supported_platforms
215 }
216
217 fn lua(&self) -> &PackageVersionReq {
218 &self.lua
219 }
220
221 fn dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
222 &self.dependencies
223 }
224
225 fn build_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
226 &self.build_dependencies
227 }
228
229 fn external_dependencies(&self) -> &PerPlatform<HashMap<String, ExternalDependencySpec>> {
230 &self.external_dependencies
231 }
232
233 fn test_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
234 &self.test_dependencies
235 }
236
237 fn build(&self) -> &PerPlatform<BuildSpec> {
238 &self.build
239 }
240
241 fn test(&self) -> &PerPlatform<TestSpec> {
242 &self.test
243 }
244
245 fn source(&self) -> &PerPlatform<RemoteRockSource> {
246 &self.source
247 }
248
249 fn deploy(&self) -> &PerPlatform<DeploySpec> {
250 &self.deploy
251 }
252
253 fn build_mut(&mut self) -> &mut PerPlatform<BuildSpec> {
254 &mut self.build
255 }
256
257 fn test_mut(&mut self) -> &mut PerPlatform<TestSpec> {
258 &mut self.test
259 }
260
261 fn source_mut(&mut self) -> &mut PerPlatform<RemoteRockSource> {
262 &mut self.source
263 }
264
265 fn deploy_mut(&mut self) -> &mut PerPlatform<DeploySpec> {
266 &mut self.deploy
267 }
268
269 fn format(&self) -> &Option<RockspecFormat> {
270 &self.rockspec_format
271 }
272
273 fn to_lua_remote_rockspec_string(&self) -> Result<String, Self::Error> {
274 Ok(self.raw_content.clone())
275 }
276}
277
278impl HasIntegrity for LocalLuaRockspec {
279 fn hash(&self) -> io::Result<Integrity> {
280 Ok(Integrity::from(&self.raw_content))
281 }
282}
283
284#[derive(Clone, Debug)]
285#[cfg_attr(test, derive(PartialEq))]
286pub struct RemoteLuaRockspec {
287 local: LocalLuaRockspec,
288 source: PerPlatform<RemoteRockSource>,
289}
290
291impl UserData for RemoteLuaRockspec {
292 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
293 methods.add_method("package", |_, this, _: ()| Ok(this.local.package.clone()));
294 methods.add_method("version", |_, this, _: ()| Ok(this.local.version.clone()));
295 methods.add_method("description", |_, this, _: ()| {
296 Ok(this.local.description.clone())
297 });
298 methods.add_method("supported_platforms", |_, this, _: ()| {
299 Ok(this.local.supported_platforms.clone())
300 });
301 methods.add_method("lua", |_, this, _: ()| Ok(this.local.lua.clone()));
302 methods.add_method("dependencies", |_, this, _: ()| {
303 Ok(this.local.dependencies.clone())
304 });
305 methods.add_method("build_dependencies", |_, this, _: ()| {
306 Ok(this.local.build_dependencies.clone())
307 });
308 methods.add_method("external_dependencies", |_, this, _: ()| {
309 Ok(this.local.external_dependencies.clone())
310 });
311 methods.add_method("test_dependencies", |_, this, _: ()| {
312 Ok(this.local.test_dependencies.clone())
313 });
314 methods.add_method("build", |_, this, _: ()| Ok(this.local.build.clone()));
315 methods.add_method("source", |_, this, _: ()| Ok(this.source.clone()));
316 methods.add_method("test", |_, this, _: ()| Ok(this.local.test.clone()));
317 methods.add_method("format", |_, this, _: ()| {
318 Ok(this.local.rockspec_format.clone())
319 });
320
321 methods.add_method("to_lua_rockspec_string", |_, this, _: ()| {
322 this.to_lua_remote_rockspec_string()
323 .map_err(|err| mlua::Error::RuntimeError(err.to_string()))
324 });
325 }
326}
327
328impl RemoteLuaRockspec {
329 pub fn new(rockspec_content: &str) -> Result<Self, LuaRockspecError> {
330 let lua = Lua::new();
331 lua.load(rockspec_content).exec()?;
332
333 let globals = lua.globals();
334 let source = globals.get("source")?;
335
336 let rockspec = RemoteLuaRockspec {
337 local: LocalLuaRockspec::new(rockspec_content, ProjectRoot::new())?,
338 source,
339 };
340
341 Ok(rockspec)
342 }
343
344 pub fn from_package_and_source_spec(
345 package_spec: PackageSpec,
346 source_spec: RockSourceSpec,
347 ) -> Self {
348 let version = package_spec.version().clone();
349 let rockspec_format: RockspecFormat = "3.0".into();
350 let raw_content = format!(
351 r#"
352rockspec_format = "{}"
353package = "{}"
354version = "{}"
355{}
356build = {{
357 type = "source"
358}}"#,
359 &rockspec_format,
360 package_spec.name(),
361 &version,
362 &source_spec.display_lua(),
363 );
364
365 let source: RemoteRockSource = source_spec.into();
366
367 let local = LocalLuaRockspec {
368 rockspec_format: Some(rockspec_format),
369 package: package_spec.name().clone(),
370 version,
371 description: RockDescription::default(),
372 supported_platforms: PlatformSupport::default(),
373 lua: PackageVersionReq::Any,
374 dependencies: PerPlatform::default(),
375 build_dependencies: PerPlatform::default(),
376 external_dependencies: PerPlatform::default(),
377 test_dependencies: PerPlatform::default(),
378 build: PerPlatform::new(BuildSpec {
379 build_backend: Some(BuildBackendSpec::Source),
380 install: InstallSpec::default(),
381 copy_directories: Vec::new(),
382 patches: HashMap::new(),
383 }),
384 source: PerPlatform::new(source.clone()),
385 test: PerPlatform::default(),
386 deploy: PerPlatform::default(),
387 raw_content,
388 };
389 Self {
390 local,
391 source: PerPlatform::new(source),
392 }
393 }
394}
395
396impl Rockspec for RemoteLuaRockspec {
397 type Error = Infallible;
398
399 fn package(&self) -> &PackageName {
400 self.local.package()
401 }
402
403 fn version(&self) -> &PackageVersion {
404 self.local.version()
405 }
406
407 fn description(&self) -> &RockDescription {
408 self.local.description()
409 }
410
411 fn supported_platforms(&self) -> &PlatformSupport {
412 self.local.supported_platforms()
413 }
414
415 fn lua(&self) -> &PackageVersionReq {
416 self.local.lua()
417 }
418
419 fn dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
420 self.local.dependencies()
421 }
422
423 fn build_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
424 self.local.build_dependencies()
425 }
426
427 fn external_dependencies(&self) -> &PerPlatform<HashMap<String, ExternalDependencySpec>> {
428 self.local.external_dependencies()
429 }
430
431 fn test_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
432 self.local.test_dependencies()
433 }
434
435 fn build(&self) -> &PerPlatform<BuildSpec> {
436 self.local.build()
437 }
438
439 fn test(&self) -> &PerPlatform<TestSpec> {
440 self.local.test()
441 }
442
443 fn source(&self) -> &PerPlatform<RemoteRockSource> {
444 &self.source
445 }
446
447 fn deploy(&self) -> &PerPlatform<DeploySpec> {
448 self.local.deploy()
449 }
450
451 fn build_mut(&mut self) -> &mut PerPlatform<BuildSpec> {
452 self.local.build_mut()
453 }
454
455 fn test_mut(&mut self) -> &mut PerPlatform<TestSpec> {
456 self.local.test_mut()
457 }
458
459 fn source_mut(&mut self) -> &mut PerPlatform<RemoteRockSource> {
460 &mut self.source
461 }
462
463 fn deploy_mut(&mut self) -> &mut PerPlatform<DeploySpec> {
464 self.local.deploy_mut()
465 }
466
467 fn format(&self) -> &Option<RockspecFormat> {
468 self.local.format()
469 }
470
471 fn to_lua_remote_rockspec_string(&self) -> Result<String, Self::Error> {
472 Ok(self.local.raw_content.clone())
473 }
474}
475
476#[derive(Error, Debug)]
477pub enum LuaVersionError {
478 #[error("The lua version {0} is not supported by {1} version {1}!")]
479 LuaVersionUnsupported(LuaVersion, PackageName, PackageVersion),
480 #[error(transparent)]
481 LuaVersionUnset(#[from] LuaVersionUnset),
482}
483
484impl HasIntegrity for RemoteLuaRockspec {
485 fn hash(&self) -> io::Result<Integrity> {
486 Ok(Integrity::from(&self.local.raw_content))
487 }
488}
489
490#[derive(Clone, Deserialize, Debug, PartialEq, Default)]
491pub struct RockDescription {
492 pub summary: Option<String>,
494 pub detailed: Option<String>,
496 pub license: Option<String>,
498 #[serde(default, deserialize_with = "deserialize_url")]
500 pub homepage: Option<Url>,
501 pub issues_url: Option<String>,
503 pub maintainer: Option<String>,
505 #[serde(default)]
507 pub labels: Vec<String>,
508}
509
510fn deserialize_url<'de, D>(deserializer: D) -> Result<Option<Url>, D::Error>
511where
512 D: serde::Deserializer<'de>,
513{
514 let s = Option::<String>::deserialize(deserializer)?;
515 s.map(|s| Url::parse(&s).map_err(serde::de::Error::custom))
516 .transpose()
517}
518
519impl DisplayAsLuaKV for RockDescription {
520 fn display_lua(&self) -> DisplayLuaKV {
521 let mut description = Vec::new();
522
523 if let Some(summary) = &self.summary {
524 description.push(DisplayLuaKV {
525 key: "summary".to_string(),
526 value: DisplayLuaValue::String(summary.clone()),
527 })
528 }
529 if let Some(detailed) = &self.detailed {
530 description.push(DisplayLuaKV {
531 key: "detailed".to_string(),
532 value: DisplayLuaValue::String(detailed.clone()),
533 })
534 }
535 if let Some(license) = &self.license {
536 description.push(DisplayLuaKV {
537 key: "license".to_string(),
538 value: DisplayLuaValue::String(license.clone()),
539 })
540 }
541 if let Some(homepage) = &self.homepage {
542 description.push(DisplayLuaKV {
543 key: "homepage".to_string(),
544 value: DisplayLuaValue::String(homepage.to_string()),
545 })
546 }
547 if let Some(issues_url) = &self.issues_url {
548 description.push(DisplayLuaKV {
549 key: "issues_url".to_string(),
550 value: DisplayLuaValue::String(issues_url.clone()),
551 })
552 }
553 if let Some(maintainer) = &self.maintainer {
554 description.push(DisplayLuaKV {
555 key: "maintainer".to_string(),
556 value: DisplayLuaValue::String(maintainer.clone()),
557 })
558 }
559 if !self.labels.is_empty() {
560 description.push(DisplayLuaKV {
561 key: "labels".to_string(),
562 value: DisplayLuaValue::List(
563 self.labels
564 .iter()
565 .cloned()
566 .map(DisplayLuaValue::String)
567 .collect(),
568 ),
569 })
570 }
571
572 DisplayLuaKV {
573 key: "description".to_string(),
574 value: DisplayLuaValue::Table(description),
575 }
576 }
577}
578
579impl UserData for RockDescription {
580 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
581 methods.add_method("summary", |_, this, _: ()| Ok(this.summary.clone()));
582 methods.add_method("detailed", |_, this, _: ()| Ok(this.detailed.clone()));
583 methods.add_method("license", |_, this, _: ()| Ok(this.license.clone()));
584 methods.add_method("homepage", |_, this, _: ()| {
585 Ok(this.homepage.clone().map(|url| url.to_string()))
586 });
587 methods.add_method("issues_url", |_, this, _: ()| Ok(this.issues_url.clone()));
588 methods.add_method("maintainer", |_, this, _: ()| Ok(this.maintainer.clone()));
589 methods.add_method("labels", |_, this, _: ()| Ok(this.labels.clone()));
590 }
591}
592
593#[derive(Error, Debug)]
594#[error("invalid rockspec format: {0}")]
595pub struct InvalidRockspecFormat(String);
596
597#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
598pub enum RockspecFormat {
599 #[serde(rename = "1.0")]
600 _1_0,
601 #[serde(rename = "2.0")]
602 _2_0,
603 #[serde(rename = "3.0")]
604 _3_0,
605}
606
607impl FromStr for RockspecFormat {
608 type Err = InvalidRockspecFormat;
609
610 fn from_str(s: &str) -> Result<Self, Self::Err> {
611 match s {
612 "1.0" => Ok(Self::_1_0),
613 "2.0" => Ok(Self::_2_0),
614 "3.0" => Ok(Self::_3_0),
615 txt => Err(InvalidRockspecFormat(txt.to_string())),
616 }
617 }
618}
619
620impl From<&str> for RockspecFormat {
621 fn from(s: &str) -> Self {
622 Self::from_str(s).unwrap()
623 }
624}
625
626impl FromLua for RockspecFormat {
627 fn from_lua(
628 value: mlua::prelude::LuaValue,
629 lua: &mlua::prelude::Lua,
630 ) -> mlua::prelude::LuaResult<Self> {
631 let s = String::from_lua(value, lua)?;
632 Self::from_str(&s).map_err(|err| mlua::Error::DeserializeError(err.to_string()))
633 }
634}
635
636impl IntoLua for RockspecFormat {
637 fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
638 self.to_string().into_lua(lua)
639 }
640}
641
642impl Display for RockspecFormat {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 match self {
645 Self::_1_0 => write!(f, "1.0"),
646 Self::_2_0 => write!(f, "2.0"),
647 Self::_3_0 => write!(f, "3.0"),
648 }
649 }
650}
651
652#[derive(Error, Debug)]
653pub enum LuaTableError {
654 #[error("could not parse {variable}. Expected list, but got {invalid_type}")]
655 ParseError {
656 variable: String,
657 invalid_type: String,
658 },
659 #[error(transparent)]
660 MLua(#[from] mlua::Error),
661}
662
663fn parse_lua_tbl_or_default<T>(lua: &Lua, lua_var_name: &str) -> Result<T, LuaTableError>
664where
665 T: Default,
666 T: DeserializeOwned,
667{
668 let ret = match lua.globals().get(lua_var_name)? {
669 Value::Nil => T::default(),
670 value @ Value::Table(_) => lua.from_value(value)?,
671 value => Err(LuaTableError::ParseError {
672 variable: lua_var_name.to_string(),
673 invalid_type: value.type_name().to_string(),
674 })?,
675 };
676 Ok(ret)
677}
678
679#[cfg(test)]
680mod tests {
681
682 use std::path::PathBuf;
683
684 use crate::git::GitSource;
685 use crate::lua_rockspec::PlatformIdentifier;
686 use crate::package::PackageSpec;
687
688 use super::*;
689
690 #[tokio::test]
691 pub async fn parse_rockspec() {
692 let rockspec_content = "
693 rockspec_format = '1.0'\n
694 package = 'foo'\n
695 version = '1.0.0-1'\n
696 source = {\n
697 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
698 }\n
699 "
700 .to_string();
701 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
702 assert_eq!(rockspec.local.rockspec_format, Some("1.0".into()));
703 assert_eq!(rockspec.local.package, "foo".into());
704 assert_eq!(rockspec.local.version, "1.0.0-1".parse().unwrap());
705 assert_eq!(rockspec.local.description, RockDescription::default());
706
707 let rockspec_content = "
708 package = 'bar'\n
709 version = '2.0.0-1'\n
710 description = {}\n
711 source = {\n
712 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
713 }\n
714 "
715 .to_string();
716 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
717 assert_eq!(rockspec.local.rockspec_format, None);
718 assert_eq!(rockspec.local.package, "bar".into());
719 assert_eq!(rockspec.local.version, "2.0.0-1".parse().unwrap());
720 assert_eq!(rockspec.local.description, RockDescription::default());
721
722 let rockspec_content = "
723 package = 'rocks.nvim'\n
724 version = '3.0.0-1'\n
725 description = {\n
726 summary = 'some summary',
727 detailed = 'some detailed description',
728 license = 'MIT',
729 homepage = 'https://github.com/nvim-neorocks/rocks.nvim',
730 issues_url = 'https://github.com/nvim-neorocks/rocks.nvim/issues',
731 maintainer = 'neorocks',
732 }\n
733 source = {\n
734 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
735 }\n
736 "
737 .to_string();
738 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
739 assert_eq!(rockspec.local.rockspec_format, None);
740 assert_eq!(rockspec.local.package, "rocks.nvim".into());
741 assert_eq!(rockspec.local.version, "3.0.0-1".parse().unwrap());
742 let expected_description = RockDescription {
743 summary: Some("some summary".into()),
744 detailed: Some("some detailed description".into()),
745 license: Some("MIT".into()),
746 homepage: Some(Url::parse("https://github.com/nvim-neorocks/rocks.nvim").unwrap()),
747 issues_url: Some("https://github.com/nvim-neorocks/rocks.nvim/issues".into()),
748 maintainer: Some("neorocks".into()),
749 labels: Vec::new(),
750 };
751 assert_eq!(rockspec.local.description, expected_description);
752
753 let rockspec_content = "
754 package = 'rocks.nvim'\n
755 version = '3.0.0-1'\n
756 description = {\n
757 summary = 'some summary',
758 detailed = 'some detailed description',
759 license = 'MIT',
760 homepage = 'https://github.com/nvim-neorocks/rocks.nvim',
761 issues_url = 'https://github.com/nvim-neorocks/rocks.nvim/issues',
762 maintainer = 'neorocks',
763 labels = {},
764 }\n
765 external_dependencies = { FOO = { library = 'foo' } }\n
766 source = {\n
767 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
768 }\n
769 "
770 .to_string();
771 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
772 assert_eq!(rockspec.local.rockspec_format, None);
773 assert_eq!(rockspec.local.package, "rocks.nvim".into());
774 assert_eq!(rockspec.local.version, "3.0.0-1".parse().unwrap());
775 let expected_description = RockDescription {
776 summary: Some("some summary".into()),
777 detailed: Some("some detailed description".into()),
778 license: Some("MIT".into()),
779 homepage: Some(Url::parse("https://github.com/nvim-neorocks/rocks.nvim").unwrap()),
780 issues_url: Some("https://github.com/nvim-neorocks/rocks.nvim/issues".into()),
781 maintainer: Some("neorocks".into()),
782 labels: Vec::new(),
783 };
784 assert_eq!(rockspec.local.description, expected_description);
785 assert_eq!(
786 *rockspec
787 .local
788 .external_dependencies
789 .default
790 .get("FOO")
791 .unwrap(),
792 ExternalDependencySpec {
793 library: Some("foo".into()),
794 header: None
795 }
796 );
797
798 let rockspec_content = "
799 package = 'rocks.nvim'\n
800 version = '3.0.0-1'\n
801 description = {\n
802 summary = 'some summary',
803 detailed = 'some detailed description',
804 license = 'MIT',
805 homepage = 'https://github.com/nvim-neorocks/rocks.nvim',
806 issues_url = 'https://github.com/nvim-neorocks/rocks.nvim/issues',
807 maintainer = 'neorocks',
808 labels = { 'package management', },
809 }\n
810 supported_platforms = { 'unix', '!windows' }\n
811 dependencies = { 'neorg ~> 6' }\n
812 build_dependencies = { 'foo' }\n
813 external_dependencies = { FOO = { header = 'foo.h' } }\n
814 test_dependencies = { 'busted >= 2.0.0' }\n
815 source = {\n
816 url = 'git+https://github.com/nvim-neorocks/rocks.nvim',\n
817 hash = 'sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=',\n
818 }\n
819 "
820 .to_string();
821 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
822 assert_eq!(rockspec.local.rockspec_format, None);
823 assert_eq!(rockspec.local.package, "rocks.nvim".into());
824 assert_eq!(rockspec.local.version, "3.0.0-1".parse().unwrap());
825 let expected_description = RockDescription {
826 summary: Some("some summary".into()),
827 detailed: Some("some detailed description".into()),
828 license: Some("MIT".into()),
829 homepage: Some(Url::parse("https://github.com/nvim-neorocks/rocks.nvim").unwrap()),
830 issues_url: Some("https://github.com/nvim-neorocks/rocks.nvim/issues".into()),
831 maintainer: Some("neorocks".into()),
832 labels: vec!["package management".into()],
833 };
834 assert_eq!(rockspec.local.description, expected_description);
835 assert!(rockspec
836 .local
837 .supported_platforms
838 .is_supported(&PlatformIdentifier::Unix));
839 assert!(!rockspec
840 .local
841 .supported_platforms
842 .is_supported(&PlatformIdentifier::Windows));
843 let neorg = PackageSpec::parse("neorg".into(), "6.0.0".into()).unwrap();
844 assert!(rockspec
845 .local
846 .dependencies
847 .default
848 .into_iter()
849 .any(|dep| dep.matches(&neorg)));
850 let foo = PackageSpec::parse("foo".into(), "1.0.0".into()).unwrap();
851 assert!(rockspec
852 .local
853 .build_dependencies
854 .default
855 .into_iter()
856 .any(|dep| dep.matches(&foo)));
857 let busted = PackageSpec::parse("busted".into(), "2.2.0".into()).unwrap();
858 assert_eq!(
859 *rockspec
860 .local
861 .external_dependencies
862 .default
863 .get("FOO")
864 .unwrap(),
865 ExternalDependencySpec {
866 header: Some("foo.h".into()),
867 library: None
868 }
869 );
870 assert!(rockspec
871 .local
872 .test_dependencies
873 .default
874 .into_iter()
875 .any(|dep| dep.matches(&busted)));
876
877 let rockspec_content = "
878 rockspec_format = '1.0'\n
879 package = 'foo'\n
880 version = '1.0.0-1'\n
881 source = {\n
882 url = 'git+https://hub.com/example-project/',\n
883 branch = 'bar',\n
884 }\n
885 "
886 .to_string();
887 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
888 assert_eq!(
889 rockspec.local.source.default.source_spec,
890 RockSourceSpec::Git(GitSource {
891 url: "https://hub.com/example-project/".parse().unwrap(),
892 checkout_ref: Some("bar".into())
893 })
894 );
895 assert_eq!(rockspec.local.test, PerPlatform::default());
896 let rockspec_content = "
897 rockspec_format = '1.0'\n
898 package = 'foo'\n
899 version = '1.0.0-1'\n
900 source = {\n
901 url = 'git+https://hub.com/example-project/',\n
902 tag = 'bar',\n
903 }\n
904 "
905 .to_string();
906 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
907 assert_eq!(
908 rockspec.local.source.default.source_spec,
909 RockSourceSpec::Git(GitSource {
910 url: "https://hub.com/example-project/".parse().unwrap(),
911 checkout_ref: Some("bar".into())
912 })
913 );
914 let rockspec_content = "
915 rockspec_format = '1.0'\n
916 package = 'foo'\n
917 version = '1.0.0-1'\n
918 source = {\n
919 url = 'git+https://hub.com/example-project/',\n
920 branch = 'bar',\n
921 tag = 'baz',\n
922 }\n
923 "
924 .to_string();
925 let _rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap_err();
926 let rockspec_content = "
927 rockspec_format = '1.0'\n
928 package = 'foo'\n
929 version = '1.0.0-1'\n
930 source = {\n
931 url = 'git+https://hub.com/example-project/',\n
932 tag = 'bar',\n
933 file = 'foo.tar.gz',\n
934 }\n
935 build = {\n
936 install = {\n
937 conf = {['foo.bar'] = 'config/bar.toml'},\n
938 },\n
939 }\n
940 "
941 .to_string();
942 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
943 assert_eq!(
944 rockspec.local.source.default.archive_name,
945 Some("foo.tar.gz".into())
946 );
947 let foo_bar_path = rockspec
948 .local
949 .build
950 .default
951 .install
952 .conf
953 .get("foo.bar")
954 .unwrap();
955 assert_eq!(*foo_bar_path, PathBuf::from("config/bar.toml"));
956 let rockspec_content = "
957 rockspec_format = '1.0'\n
958 package = 'foo'\n
959 version = '1.0.0-1'\n
960 source = {\n
961 url = 'git+https://hub.com/example-project/foo.zip',\n
962 }\n
963 build = {\n
964 install = {\n
965 lua = {['foo.bar'] = 'src/bar.lua'},\n
966 bin = {['foo.bar'] = 'bin/bar'},\n
967 },\n
968 }\n
969 "
970 .to_string();
971 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
972 assert!(matches!(
973 rockspec.local.build.default.build_backend,
974 Some(BuildBackendSpec::Builtin { .. })
975 ));
976 let foo_bar_path = rockspec
977 .local
978 .build
979 .default
980 .install
981 .lua
982 .get(&LuaModule::from_str("foo.bar").unwrap())
983 .unwrap();
984 assert_eq!(*foo_bar_path, PathBuf::from("src/bar.lua"));
985 let foo_bar_path = rockspec
986 .local
987 .build
988 .default
989 .install
990 .bin
991 .get("foo.bar")
992 .unwrap();
993 assert_eq!(*foo_bar_path, PathBuf::from("bin/bar"));
994 let rockspec_content = "
995 rockspec_format = '1.0'\n
996 package = 'foo'\n
997 version = '1.0.0-1'\n
998 source = {\n
999 url = 'git+https://hub.com/example-project/',\n
1000 }\n
1001 build = {\n
1002 copy_directories = { 'lua' },\n
1003 }\n
1004 "
1005 .to_string();
1006 let _rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap_err();
1007 let rockspec_content = "
1008 rockspec_format = '1.0'\n
1009 package = 'foo'\n
1010 version = '1.0.0-1'\n
1011 source = {\n
1012 url = 'git+https://hub.com/example-project/',\n
1013 }\n
1014 build = {\n
1015 copy_directories = { 'lib' },\n
1016 }\n
1017 "
1018 .to_string();
1019 let _rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap_err();
1020 let rockspec_content = "
1021 rockspec_format = '1.0'\n
1022 package = 'foo'\n
1023 version = '1.0.0-1'\n
1024 source = {\n
1025 url = 'git+https://hub.com/example-project/',\n
1026 }\n
1027 build = {\n
1028 copy_directories = { 'rock_manifest' },\n
1029 }\n
1030 "
1031 .to_string();
1032 let _rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap_err();
1033 let rockspec_content = "
1034 rockspec_format = '1.0'\n
1035 package = 'foo'\n
1036 version = '1.0.0-1'\n
1037 source = {\n
1038 url = 'git+https://hub.com/example-project/foo.zip',\n
1039 dir = 'baz',\n
1040 }\n
1041 build = {\n
1042 type = 'make',\n
1043 install = {\n
1044 lib = {['foo.bar'] = 'lib/bar.so'},\n
1045 },\n
1046 copy_directories = {\n
1047 'plugin',\n
1048 'ftplugin',\n
1049 },\n
1050 patches = {\n
1051 ['lua51-support.diff'] = [[\n
1052 --- before.c\n
1053 +++ path/to/after.c\n
1054 ]],\n
1055 },\n
1056 }\n
1057 "
1058 .to_string();
1059 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1060 assert_eq!(rockspec.local.source.default.unpack_dir, Some("baz".into()));
1061 assert_eq!(
1062 rockspec.local.build.default.build_backend,
1063 Some(BuildBackendSpec::Make(MakeBuildSpec::default()))
1064 );
1065 let foo_bar_path = rockspec
1066 .local
1067 .build
1068 .default
1069 .install
1070 .lib
1071 .get(&LuaModule::from_str("foo.bar").unwrap())
1072 .unwrap();
1073 assert_eq!(*foo_bar_path, PathBuf::from("lib/bar.so"));
1074 let copy_directories = rockspec.local.build.default.copy_directories;
1075 assert_eq!(
1076 copy_directories,
1077 vec![PathBuf::from("plugin"), PathBuf::from("ftplugin")]
1078 );
1079 let patches = rockspec.local.build.default.patches;
1080 let _patch = patches.get(&PathBuf::from("lua51-support.diff")).unwrap();
1081 let rockspec_content = "
1082 rockspec_format = '1.0'\n
1083 package = 'foo'\n
1084 version = '1.0.0-1'\n
1085 source = {\n
1086 url = 'git+https://hub.com/example-project/foo.zip',\n
1087 }\n
1088 build = {\n
1089 type = 'cmake',\n
1090 }\n
1091 "
1092 .to_string();
1093 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1094 assert_eq!(
1095 rockspec.local.build.default.build_backend,
1096 Some(BuildBackendSpec::CMake(CMakeBuildSpec::default()))
1097 );
1098 let rockspec_content = "
1099 rockspec_format = '1.0'\n
1100 package = 'foo'\n
1101 version = '1.0.0-1'\n
1102 source = {\n
1103 url = 'git+https://hub.com/example-project/foo.zip',\n
1104 }\n
1105 build = {\n
1106 type = 'command',\n
1107 build_command = 'foo',\n
1108 install_command = 'bar',\n
1109 }\n
1110 "
1111 .to_string();
1112 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1113 assert!(matches!(
1114 rockspec.local.build.default.build_backend,
1115 Some(BuildBackendSpec::Command(CommandBuildSpec { .. }))
1116 ));
1117 let rockspec_content = "
1118 rockspec_format = '1.0'\n
1119 package = 'foo'\n
1120 version = '1.0.0-1'\n
1121 source = {\n
1122 url = 'git+https://hub.com/example-project/foo.zip',\n
1123 }\n
1124 build = {\n
1125 type = 'command',\n
1126 install_command = 'foo',\n
1127 }\n
1128 "
1129 .to_string();
1130 RemoteLuaRockspec::new(&rockspec_content).unwrap();
1131 let rockspec_content = "
1132 rockspec_format = '1.0'\n
1133 package = 'foo'\n
1134 version = '1.0.0-1'\n
1135 source = {\n
1136 url = 'git+https://hub.com/example-project/foo.zip',\n
1137 }\n
1138 build = {\n
1139 type = 'command',\n
1140 build_command = 'foo',\n
1141 }\n
1142 "
1143 .to_string();
1144 RemoteLuaRockspec::new(&rockspec_content).unwrap();
1145 let rockspec_content = "
1147 package = 'rocks'\n
1148 version = '3.0.0-1'\n
1149 dependencies = {\n
1150 'neorg ~> 6',\n
1151 'toml-edit ~> 1',\n
1152 platforms = {\n
1153 windows = {\n
1154 'neorg = 5.0.0',\n
1155 'toml = 1.0.0',\n
1156 },\n
1157 unix = {\n
1158 'neorg = 5.0.0',\n
1159 },\n
1160 linux = {\n
1161 'toml = 1.0.0',\n
1162 },\n
1163 },\n
1164 }\n
1165 source = {\n
1166 url = 'git+https://github.com/nvim-neorocks/rocks.nvim',\n
1167 hash = 'sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=',\n
1168 }\n
1169 "
1170 .to_string();
1171 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1172 let neorg_override = PackageSpec::parse("neorg".into(), "5.0.0".into()).unwrap();
1173 let toml_edit = PackageSpec::parse("toml-edit".into(), "1.0.0".into()).unwrap();
1174 let toml = PackageSpec::parse("toml".into(), "1.0.0".into()).unwrap();
1175 assert_eq!(rockspec.local.dependencies.default.len(), 2);
1176 let per_platform = &rockspec.local.dependencies.per_platform;
1177 assert_eq!(
1178 per_platform
1179 .get(&PlatformIdentifier::Windows)
1180 .unwrap()
1181 .iter()
1182 .filter(|dep| dep.matches(&neorg_override)
1183 || dep.matches(&toml_edit)
1184 || dep.matches(&toml))
1185 .count(),
1186 3
1187 );
1188 assert_eq!(
1189 per_platform
1190 .get(&PlatformIdentifier::Unix)
1191 .unwrap()
1192 .iter()
1193 .filter(|dep| dep.matches(&neorg_override)
1194 || dep.matches(&toml_edit)
1195 || dep.matches(&toml))
1196 .count(),
1197 2
1198 );
1199 assert_eq!(
1200 per_platform
1201 .get(&PlatformIdentifier::Linux)
1202 .unwrap()
1203 .iter()
1204 .filter(|dep| dep.matches(&neorg_override)
1205 || dep.matches(&toml_edit)
1206 || dep.matches(&toml))
1207 .count(),
1208 3
1209 );
1210 let rockspec_content = "
1211 package = 'rocks'\n
1212 version = '3.0.0-1'\n
1213 external_dependencies = {\n
1214 FOO = { library = 'foo' },\n
1215 platforms = {\n
1216 windows = {\n
1217 FOO = { library = 'foo.dll' },\n
1218 },\n
1219 unix = {\n
1220 BAR = { header = 'bar.h' },\n
1221 },\n
1222 linux = {\n
1223 FOO = { library = 'foo.so' },\n
1224 },\n
1225 },\n
1226 }\n
1227 source = {\n
1228 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
1229 }\n
1230 "
1231 .to_string();
1232 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1233 assert_eq!(
1234 *rockspec
1235 .local
1236 .external_dependencies
1237 .default
1238 .get("FOO")
1239 .unwrap(),
1240 ExternalDependencySpec {
1241 library: Some("foo".into()),
1242 header: None
1243 }
1244 );
1245 let per_platform = rockspec.local.external_dependencies.per_platform;
1246 assert_eq!(
1247 *per_platform
1248 .get(&PlatformIdentifier::Windows)
1249 .and_then(|it| it.get("FOO"))
1250 .unwrap(),
1251 ExternalDependencySpec {
1252 library: Some("foo.dll".into()),
1253 header: None
1254 }
1255 );
1256 assert_eq!(
1257 *per_platform
1258 .get(&PlatformIdentifier::Unix)
1259 .and_then(|it| it.get("FOO"))
1260 .unwrap(),
1261 ExternalDependencySpec {
1262 library: Some("foo".into()),
1263 header: None
1264 }
1265 );
1266 assert_eq!(
1267 *per_platform
1268 .get(&PlatformIdentifier::Unix)
1269 .and_then(|it| it.get("BAR"))
1270 .unwrap(),
1271 ExternalDependencySpec {
1272 header: Some("bar.h".into()),
1273 library: None
1274 }
1275 );
1276 assert_eq!(
1277 *per_platform
1278 .get(&PlatformIdentifier::Linux)
1279 .and_then(|it| it.get("BAR"))
1280 .unwrap(),
1281 ExternalDependencySpec {
1282 header: Some("bar.h".into()),
1283 library: None
1284 }
1285 );
1286 assert_eq!(
1287 *per_platform
1288 .get(&PlatformIdentifier::Linux)
1289 .and_then(|it| it.get("FOO"))
1290 .unwrap(),
1291 ExternalDependencySpec {
1292 library: Some("foo.so".into()),
1293 header: None
1294 }
1295 );
1296 let rockspec_content = "
1297 rockspec_format = '1.0'\n
1298 package = 'foo'\n
1299 version = '1.0.0-1'\n
1300 source = {\n
1301 url = 'git+https://hub.com/example-project/.git',\n
1302 branch = 'bar',\n
1303 platforms = {\n
1304 macosx = {\n
1305 branch = 'mac',\n
1306 },\n
1307 windows = {\n
1308 url = 'git+https://winhub.com/example-project/.git',\n
1309 branch = 'win',\n
1310 },\n
1311 },\n
1312 }\n
1313 "
1314 .to_string();
1315 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1316 assert_eq!(
1317 rockspec.local.source.default.source_spec,
1318 RockSourceSpec::Git(GitSource {
1319 url: "https://hub.com/example-project/.git".parse().unwrap(),
1320 checkout_ref: Some("bar".into())
1321 })
1322 );
1323 assert_eq!(
1324 rockspec
1325 .source
1326 .per_platform
1327 .get(&PlatformIdentifier::MacOSX)
1328 .map(|it| it.source_spec.clone())
1329 .unwrap(),
1330 RockSourceSpec::Git(GitSource {
1331 url: "https://hub.com/example-project/.git".parse().unwrap(),
1332 checkout_ref: Some("mac".into())
1333 })
1334 );
1335 assert_eq!(
1336 rockspec
1337 .source
1338 .per_platform
1339 .get(&PlatformIdentifier::Windows)
1340 .map(|it| it.source_spec.clone())
1341 .unwrap(),
1342 RockSourceSpec::Git(GitSource {
1343 url: "https://winhub.com/example-project/.git".parse().unwrap(),
1344 checkout_ref: Some("win".into())
1345 })
1346 );
1347 let rockspec_content = "
1348 rockspec_format = '1.0'\n
1349 package = 'foo'\n
1350 version = '1.0.0-1'\n
1351 source = { url = 'git+https://hub.com/example-project/foo.zip' }\n
1352 build = {\n
1353 type = 'make',\n
1354 install = {\n
1355 lib = {['foo.bar'] = 'lib/bar.so'},\n
1356 },\n
1357 copy_directories = { 'plugin' },\n
1358 platforms = {\n
1359 unix = {\n
1360 copy_directories = { 'ftplugin' },\n
1361 },\n
1362 linux = {\n
1363 copy_directories = { 'foo' },\n
1364 },\n
1365 },\n
1366 }\n
1367 "
1368 .to_string();
1369 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1370 let per_platform = rockspec.local.build.per_platform;
1371 let unix = per_platform.get(&PlatformIdentifier::Unix).unwrap();
1372 assert_eq!(
1373 unix.copy_directories,
1374 vec![PathBuf::from("plugin"), PathBuf::from("ftplugin")]
1375 );
1376 let linux = per_platform.get(&PlatformIdentifier::Linux).unwrap();
1377 assert_eq!(
1378 linux.copy_directories,
1379 vec![
1380 PathBuf::from("plugin"),
1381 PathBuf::from("foo"),
1382 PathBuf::from("ftplugin")
1383 ]
1384 );
1385 let rockspec_content = "
1386 package = 'foo'\n
1387 version = '1.0.0-1'\n
1388 source = { url = 'git+https://hub.com/example-project/foo.zip' }\n
1389 build = {\n
1390 type = 'builtin',\n
1391 modules = {\n
1392 cjson = {\n
1393 sources = { 'lua_cjson.c', 'strbuf.c', 'fpconv.c' },\n
1394 }\n
1395 },\n
1396 platforms = {\n
1397 win32 = { modules = { cjson = { defines = {\n
1398 'DISABLE_INVALID_NUMBERS', 'USE_INTERNAL_ISINF'\n
1399 } } } }\n
1400 },\n
1401 }\n
1402 "
1403 .to_string();
1404 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1405 let per_platform = &rockspec.local.build.per_platform;
1406 let win32 = per_platform.get(&PlatformIdentifier::Windows).unwrap();
1407 assert_eq!(
1408 win32.build_backend,
1409 Some(BuildBackendSpec::Builtin(BuiltinBuildSpec {
1410 modules: vec![(
1411 LuaModule::from_str("cjson").unwrap(),
1412 ModuleSpec::ModulePaths(ModulePaths {
1413 sources: vec!["lua_cjson.c".into(), "strbuf.c".into(), "fpconv.c".into()],
1414 libraries: Vec::default(),
1415 defines: vec![
1416 ("DISABLE_INVALID_NUMBERS".into(), None),
1417 ("USE_INTERNAL_ISINF".into(), None)
1418 ],
1419 incdirs: Vec::default(),
1420 libdirs: Vec::default(),
1421 })
1422 )]
1423 .into_iter()
1424 .collect()
1425 }))
1426 );
1427 let rockspec_content = "
1428 rockspec_format = '1.0'\n
1429 package = 'foo'\n
1430 version = '1.0.0-1'\n
1431 deploy = {\n
1432 wrap_bin_scripts = false,\n
1433 }\n
1434 source = { url = 'git+https://hub.com/example-project/foo.zip' }\n
1435 ";
1436 let rockspec = RemoteLuaRockspec::new(rockspec_content).unwrap();
1437 let deploy_spec = &rockspec.deploy().current_platform();
1438 assert!(!deploy_spec.wrap_bin_scripts);
1439 }
1440
1441 #[tokio::test]
1442 pub async fn parse_scm_rockspec() {
1443 let rockspec_content = "
1444 package = 'foo'\n
1445 version = 'scm-1'\n
1446 source = {\n
1447 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
1448 }\n
1449 "
1450 .to_string();
1451 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1452 assert_eq!(rockspec.local.package, "foo".into());
1453 assert_eq!(rockspec.local.version, "scm-1".parse().unwrap());
1454 }
1455
1456 #[tokio::test]
1457 pub async fn regression_luasystem() {
1458 let rockspec_content =
1459 String::from_utf8(std::fs::read("resources/test/luasystem-0.4.4-1.rockspec").unwrap())
1460 .unwrap();
1461 let rockspec = RemoteLuaRockspec::new(&rockspec_content).unwrap();
1462 let build_spec = rockspec.local.build.current_platform();
1463 assert!(matches!(
1464 build_spec.build_backend,
1465 Some(BuildBackendSpec::Builtin { .. })
1466 ));
1467 if let Some(BuildBackendSpec::Builtin(BuiltinBuildSpec { modules })) =
1468 &build_spec.build_backend
1469 {
1470 assert_eq!(
1471 modules.get(&LuaModule::from_str("system.init").unwrap()),
1472 Some(&ModuleSpec::SourcePath("system/init.lua".into()))
1473 );
1474 assert_eq!(
1475 modules.get(&LuaModule::from_str("system.core").unwrap()),
1476 Some(&ModuleSpec::ModulePaths(ModulePaths {
1477 sources: vec![
1478 "src/core.c".into(),
1479 "src/compat.c".into(),
1480 "src/time.c".into(),
1481 "src/environment.c".into(),
1482 "src/random.c".into(),
1483 "src/term.c".into(),
1484 "src/bitflags.c".into(),
1485 "src/wcwidth.c".into(),
1486 ],
1487 defines: luasystem_expected_defines(),
1488 libraries: luasystem_expected_libraries(),
1489 incdirs: luasystem_expected_incdirs(),
1490 libdirs: luasystem_expected_libdirs(),
1491 }))
1492 );
1493 }
1494 if let Some(BuildBackendSpec::Builtin(BuiltinBuildSpec { modules })) = &rockspec
1495 .local
1496 .build
1497 .get(&PlatformIdentifier::Windows)
1498 .build_backend
1499 {
1500 if let ModuleSpec::ModulePaths(paths) = modules
1501 .get(&LuaModule::from_str("system.core").unwrap())
1502 .unwrap()
1503 {
1504 assert_eq!(paths.libraries, luasystem_expected_windows_libraries());
1505 };
1506 }
1507 if let Some(BuildBackendSpec::Builtin(BuiltinBuildSpec { modules })) = &rockspec
1508 .local
1509 .build
1510 .get(&PlatformIdentifier::Win32)
1511 .build_backend
1512 {
1513 if let ModuleSpec::ModulePaths(paths) = modules
1514 .get(&LuaModule::from_str("system.core").unwrap())
1515 .unwrap()
1516 {
1517 assert_eq!(paths.libraries, luasystem_expected_windows_libraries());
1518 };
1519 }
1520 }
1521
1522 fn luasystem_expected_defines() -> Vec<(String, Option<String>)> {
1523 if cfg!(target_os = "windows") {
1524 vec![
1525 ("WINVER".into(), Some("0x0600".into())),
1526 ("_WIN32_WINNT".into(), Some("0x0600".into())),
1527 ]
1528 } else {
1529 Vec::default()
1530 }
1531 }
1532
1533 fn luasystem_expected_windows_libraries() -> Vec<PathBuf> {
1534 vec!["advapi32".into(), "winmm".into()]
1535 }
1536 fn luasystem_expected_libraries() -> Vec<PathBuf> {
1537 if cfg!(target_os = "linux") {
1538 vec!["rt".into()]
1539 } else if cfg!(target_os = "windows") {
1540 luasystem_expected_windows_libraries()
1541 } else {
1542 Vec::default()
1543 }
1544 }
1545
1546 fn luasystem_expected_incdirs() -> Vec<PathBuf> {
1547 Vec::default()
1548 }
1549
1550 fn luasystem_expected_libdirs() -> Vec<PathBuf> {
1551 Vec::default()
1552 }
1553
1554 #[tokio::test]
1555 pub async fn rust_mlua_rockspec() {
1556 let rockspec_content = "
1557 package = 'foo'\n
1558 version = 'scm-1'\n
1559 source = {\n
1560 url = 'https://github.com/nvim-neorocks/rocks.nvim/archive/1.0.0/rocks.nvim.zip',\n
1561 }\n
1562 build = {
1563 type = 'rust-mlua',
1564 modules = {
1565 'foo',
1566 bar = 'baz',
1567 },
1568 target_path = 'path/to/cargo/target/directory',
1569 default_features = false,
1570 include = {
1571 'file.lua',
1572 ['path/to/another/file.lua'] = 'another-file.lua',
1573 },
1574 features = {'extra', 'features'},
1575 }
1576 ";
1577 let rockspec = RemoteLuaRockspec::new(rockspec_content).unwrap();
1578 let build_spec = rockspec.local.build.current_platform();
1579 if let Some(BuildBackendSpec::RustMlua(build_spec)) = build_spec.build_backend.to_owned() {
1580 assert_eq!(
1581 build_spec.modules.get("foo").unwrap(),
1582 &PathBuf::from(format!("libfoo.{}", std::env::consts::DLL_EXTENSION))
1583 );
1584 assert_eq!(
1585 build_spec.modules.get("bar").unwrap(),
1586 &PathBuf::from(format!("libbaz.{}", std::env::consts::DLL_EXTENSION))
1587 );
1588 assert_eq!(
1589 build_spec.include.get(&PathBuf::from("file.lua")).unwrap(),
1590 &PathBuf::from("file.lua")
1591 );
1592 assert_eq!(
1593 build_spec
1594 .include
1595 .get(&PathBuf::from("path/to/another/file.lua"))
1596 .unwrap(),
1597 &PathBuf::from("another-file.lua")
1598 );
1599 } else {
1600 panic!("Expected RustMlua build backend");
1601 }
1602 }
1603
1604 #[tokio::test]
1605 pub async fn regression_ltui() {
1606 let content =
1607 String::from_utf8(std::fs::read("resources/test/ltui-2.8-2.rockspec").unwrap())
1608 .unwrap();
1609 RemoteLuaRockspec::new(&content).unwrap();
1610 }
1611
1612 #[tokio::test]
1615 pub async fn regression_off_spec_install_binaries() {
1616 let rockspec_content = r#"
1617 package = "WSAPI"
1618 version = "1.7-1"
1619
1620 source = {
1621 url = "git://github.com/keplerproject/wsapi",
1622 tag = "v1.7",
1623 }
1624
1625 build = {
1626 type = "builtin",
1627 modules = {
1628 ["wsapi"] = "src/wsapi.lua",
1629 },
1630 -- Offending Line
1631 install = { bin = { "src/launcher/wsapi.cgi" } }
1632 }
1633 "#;
1634
1635 let rockspec = RemoteLuaRockspec::new(rockspec_content).unwrap();
1636
1637 assert_eq!(
1638 rockspec.build().current_platform().install.bin,
1639 HashMap::from([("wsapi".into(), PathBuf::from("src/launcher/wsapi.cgi"))])
1640 );
1641 }
1642
1643 #[tokio::test]
1644 pub async fn regression_external_dependencies() {
1645 let content =
1646 String::from_utf8(std::fs::read("resources/test/luaossl-20220711-0.rockspec").unwrap())
1647 .unwrap();
1648 let rockspec = RemoteLuaRockspec::new(&content).unwrap();
1649 if cfg!(target_family = "unix") {
1650 assert_eq!(
1651 rockspec
1652 .local
1653 .external_dependencies
1654 .current_platform()
1655 .get("OPENSSL")
1656 .unwrap(),
1657 &ExternalDependencySpec {
1658 library: Some("ssl".into()),
1659 header: Some("openssl/ssl.h".into()),
1660 }
1661 );
1662 }
1663 let per_platform = rockspec.local.external_dependencies.per_platform;
1664 assert_eq!(
1665 *per_platform
1666 .get(&PlatformIdentifier::Windows)
1667 .and_then(|it| it.get("OPENSSL"))
1668 .unwrap(),
1669 ExternalDependencySpec {
1670 library: Some("libeay32".into()),
1671 header: Some("openssl/ssl.h".into()),
1672 }
1673 );
1674 }
1675
1676 #[tokio::test]
1677 pub async fn remote_lua_rockspec_from_package_and_source_spec() {
1678 let package_req = "foo@1.0.5".parse().unwrap();
1679 let source = GitSource {
1680 url: "https://hub.com/example-project.git".parse().unwrap(),
1681 checkout_ref: Some("1.0.5".into()),
1682 };
1683 let source_spec = RockSourceSpec::Git(source);
1684 let rockspec =
1685 RemoteLuaRockspec::from_package_and_source_spec(package_req, source_spec.clone());
1686 let generated_rockspec_str = rockspec.local.raw_content;
1687 let rockspec2 = RemoteLuaRockspec::new(&generated_rockspec_str).unwrap();
1688 assert_eq!(rockspec2.local.package, "foo".into());
1689 assert_eq!(rockspec2.local.version, "1.0.5".parse().unwrap());
1690 assert_eq!(rockspec2.local.source, PerPlatform::new(source_spec.into()));
1691 }
1692}