1use crate::git::shorthand::GitUrlShorthand;
4use crate::git::GitSource;
5use crate::hash::HasIntegrity;
6use crate::lockfile::OptState;
7use crate::lockfile::PinnedState;
8use crate::lua_rockspec::DeploySpec;
9use crate::lua_rockspec::LocalLuaRockspec;
10use crate::lua_rockspec::LocalRockSource;
11use crate::lua_rockspec::LuaRockspecError;
12use crate::lua_rockspec::RemoteLuaRockspec;
13use crate::lua_rockspec::RockSourceSpec;
14use crate::operations::RunCommand;
15use crate::package::PackageNameList;
16use crate::rockspec::lua_dependency::LuaDependencySpec;
17use std::io;
18use std::{collections::HashMap, path::PathBuf};
19
20use itertools::Itertools;
21use mlua::ExternalResult;
22use mlua::UserData;
23use nonempty::NonEmpty;
24use serde::de;
25use serde::{Deserialize, Deserializer};
26use ssri::Integrity;
27use thiserror::Error;
28
29use crate::{
30 config::{Config, LuaVersion},
31 lua_rockspec::{
32 BuildSpec, BuildSpecInternal, BuildSpecInternalError, DisplayAsLuaKV, ExternalDependencies,
33 ExternalDependencySpec, FromPlatformOverridable, LuaVersionError, PartialLuaRockspec,
34 PerPlatform, PlatformIdentifier, PlatformSupport, PlatformValidationError,
35 RemoteRockSource, RockDescription, RockSourceError, RockspecFormat, TestSpec,
36 TestSpecDecodeError, TestSpecInternal,
37 },
38 package::{
39 BuildDependencies, Dependencies, PackageName, PackageReq, PackageVersion,
40 PackageVersionReq, TestDependencies,
41 },
42 rockspec::{LuaVersionCompatibility, Rockspec},
43};
44
45use super::gen::GenerateSourceError;
46use super::gen::RockSourceTemplate;
47use super::r#gen::GenerateVersionError;
48use super::r#gen::PackageVersionTemplate;
49use super::ProjectRoot;
50
51pub const PROJECT_TOML: &str = "lux.toml";
52
53#[derive(Deserialize)]
54#[serde(untagged)]
55#[allow(clippy::large_enum_variant)] enum DependencyEntry {
57 Simple(PackageVersionReq),
58 Detailed(DependencyTableEntry),
59}
60
61#[derive(Debug, Deserialize)]
62struct DependencyTableEntry {
63 version: PackageVersionReq,
64 #[serde(default)]
65 opt: Option<bool>,
66 #[serde(default)]
67 pin: Option<bool>,
68 #[serde(default)]
69 git: Option<GitUrlShorthand>,
70 #[serde(default)]
71 rev: Option<String>,
72}
73
74fn parse_map_to_dependency_vec_opt<'de, D>(
75 deserializer: D,
76) -> Result<Option<Vec<LuaDependencySpec>>, D::Error>
77where
78 D: Deserializer<'de>,
79{
80 let packages: Option<HashMap<PackageName, DependencyEntry>> =
81 Option::deserialize(deserializer)?;
82
83 match packages {
84 None => Ok(None),
85 Some(packages) => Ok(Some(
86 packages
87 .into_iter()
88 .map(|(name, spec)| match spec {
89 DependencyEntry::Simple(version_req) => {
90 Ok(PackageReq { name, version_req }.into())
91 }
92 DependencyEntry::Detailed(entry) => {
93 let source = match (entry.git, entry.rev) {
94 (None, None) => Ok(None),
95 (None, Some(_)) => Err(de::Error::custom(format!(
96 "dependency {} specifies a 'rev', but missing a 'git' field",
97 &name
98 ))),
99 (Some(git), Some(rev)) => Ok(Some(RockSourceSpec::Git(GitSource {
100 url: git.into(),
101 checkout_ref: Some(rev),
102 }))),
103 (Some(git), None) => Ok(Some(RockSourceSpec::Git(GitSource {
104 url: git.into(),
105 checkout_ref: Some(
106 entry
107 .version
108 .clone()
109 .to_string()
110 .trim_start_matches("=")
111 .to_string(),
112 ),
113 }))),
114 }?;
115 Ok(LuaDependencySpec {
116 package_req: PackageReq {
117 name,
118 version_req: entry.version,
119 },
120 opt: OptState::from(entry.opt.unwrap_or(false)),
121 pin: PinnedState::from(entry.pin.unwrap_or(false)),
122 source,
123 })
124 }
125 })
126 .try_collect()?,
127 )),
128 }
129}
130
131#[derive(Debug, Error)]
132pub enum ProjectTomlError {
133 #[error("error generating rockspec source:\n{0}")]
134 GenerateSource(#[from] GenerateSourceError),
135 #[error("error generating rockspec version:\n{0}")]
136 GenerateVersion(#[from] GenerateVersionError),
137}
138
139#[derive(Debug, Error)]
140pub enum LocalProjectTomlValidationError {
141 #[error("no lua version provided")]
142 NoLuaVersion,
143 #[error(transparent)]
144 TestSpecError(#[from] TestSpecDecodeError),
145 #[error(transparent)]
146 BuildSpecInternal(#[from] BuildSpecInternalError),
147 #[error(transparent)]
148 PlatformValidationError(#[from] PlatformValidationError),
149 #[error("{}copy_directories cannot contain a rockspec name", ._0.as_ref().map(|p| format!("{p}: ")).unwrap_or_default())]
150 CopyDirectoriesContainRockspecName(Option<String>),
151 #[error(transparent)]
152 RockSourceError(#[from] RockSourceError),
153 #[error("duplicate dependencies: {0}")]
154 DuplicateDependencies(PackageNameList),
155 #[error("duplicate test dependencies: {0}")]
156 DuplicateTestDependencies(PackageNameList),
157 #[error("duplicate build dependencies: {0}")]
158 DuplicateBuildDependencies(PackageNameList),
159 #[error("dependencies field cannot contain lua - please provide the version in the top-level lua field")]
160 DependenciesContainLua,
161 #[error("error generating rockspec source:\n{0}")]
162 GenerateSource(#[from] GenerateSourceError),
163 #[error("error generating rockspec version:\n{0}")]
164 GenerateVersion(#[from] GenerateVersionError),
165}
166
167#[derive(Debug, Error)]
168pub enum RemoteProjectTomlValidationError {
169 #[error("error generating rockspec source:\n{0}")]
170 GenerateSource(#[from] GenerateSourceError),
171 #[error("error generating rockspec version:\n{0}")]
172 GenerateVersion(#[from] GenerateVersionError),
173 #[error(transparent)]
174 LocalProjectTomlValidationError(#[from] LocalProjectTomlValidationError),
175}
176
177#[derive(Clone, Debug, Deserialize)]
181pub struct PartialProjectToml {
182 pub(crate) package: PackageName,
183 #[serde(default, rename = "version")]
184 pub(crate) version_template: PackageVersionTemplate,
185 #[serde(default)]
186 pub(crate) build: BuildSpecInternal,
187 pub(crate) rockspec_format: Option<RockspecFormat>,
188 #[serde(default)]
189 pub(crate) run: Option<RunSpec>,
190 #[serde(default)]
191 pub(crate) lua: Option<PackageVersionReq>,
192 #[serde(default)]
193 pub(crate) description: Option<RockDescription>,
194 #[serde(default)]
195 pub(crate) supported_platforms: Option<HashMap<PlatformIdentifier, bool>>,
196 #[serde(default, deserialize_with = "parse_map_to_dependency_vec_opt")]
197 pub(crate) dependencies: Option<Vec<LuaDependencySpec>>,
198 #[serde(default, deserialize_with = "parse_map_to_dependency_vec_opt")]
199 pub(crate) build_dependencies: Option<Vec<LuaDependencySpec>>,
200 #[serde(default)]
201 pub(crate) external_dependencies: Option<HashMap<String, ExternalDependencySpec>>,
202 #[serde(default, deserialize_with = "parse_map_to_dependency_vec_opt")]
203 pub(crate) test_dependencies: Option<Vec<LuaDependencySpec>>,
204 #[serde(default, rename = "source")]
205 pub(crate) source_template: RockSourceTemplate,
206 #[serde(default)]
207 pub(crate) test: Option<TestSpecInternal>,
208 #[serde(default)]
209 pub(crate) deploy: Option<DeploySpec>,
210
211 #[serde(skip, default = "ProjectRoot::new")]
213 pub(crate) project_root: ProjectRoot,
214}
215
216impl UserData for PartialProjectToml {
217 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
218 methods.add_method("package", |_, this, _: ()| Ok(this.package().clone()));
219 methods.add_method("to_local", |_, this, _: ()| {
220 this.into_local().into_lua_err()
221 });
222 methods.add_method("to_remote", |_, this, _: ()| {
223 this.into_remote().into_lua_err()
224 });
225 }
230}
231
232impl HasIntegrity for PartialProjectToml {
233 fn hash(&self) -> io::Result<Integrity> {
234 let toml_file = self.project_root.join(PROJECT_TOML);
235 let content = std::fs::read_to_string(&toml_file)?;
236 Ok(Integrity::from(&content))
237 }
238}
239
240impl PartialProjectToml {
241 pub(crate) fn new(str: &str, project_root: ProjectRoot) -> Result<Self, toml::de::Error> {
242 Ok(Self {
243 project_root,
244 ..toml::from_str(str)?
245 })
246 }
247
248 pub fn into_local(&self) -> Result<LocalProjectToml, LocalProjectTomlValidationError> {
251 let project_toml = self.clone();
252
253 if project_toml
255 .dependencies
256 .as_ref()
257 .is_some_and(|deps| deps.iter().any(|dep| dep.name() == &"lua".into()))
258 {
259 return Err(LocalProjectTomlValidationError::DependenciesContainLua);
260 }
261
262 let get_duplicates = |dependencies: &Option<Vec<LuaDependencySpec>>| {
263 dependencies
264 .iter()
265 .flat_map(|deps| {
266 deps.iter()
267 .map(|dep| dep.package_req().name())
268 .duplicates()
269 .cloned()
270 })
271 .collect_vec()
272 };
273 let duplicate_dependencies = get_duplicates(&self.dependencies);
274 if !duplicate_dependencies.is_empty() {
275 return Err(LocalProjectTomlValidationError::DuplicateDependencies(
276 PackageNameList::new(duplicate_dependencies),
277 ));
278 }
279 let duplicate_test_dependencies = get_duplicates(&self.test_dependencies);
280 if !duplicate_test_dependencies.is_empty() {
281 return Err(LocalProjectTomlValidationError::DuplicateTestDependencies(
282 PackageNameList::new(duplicate_test_dependencies),
283 ));
284 }
285 let duplicate_build_dependencies = get_duplicates(&self.build_dependencies);
286 if !duplicate_build_dependencies.is_empty() {
287 return Err(LocalProjectTomlValidationError::DuplicateBuildDependencies(
288 PackageNameList::new(duplicate_build_dependencies),
289 ));
290 }
291
292 let validated = LocalProjectToml {
293 internal: project_toml.clone(),
294
295 package: project_toml.package,
296 version: project_toml
297 .version_template
298 .try_generate(&self.project_root)
299 .unwrap_or(PackageVersion::default_dev_version()),
300 lua: project_toml
301 .lua
302 .ok_or(LocalProjectTomlValidationError::NoLuaVersion)?,
303 description: project_toml.description.unwrap_or_default(),
304 run: project_toml.run.map(PerPlatform::new),
305 supported_platforms: PlatformSupport::parse(
306 &project_toml
307 .supported_platforms
308 .unwrap_or_default()
309 .into_iter()
310 .map(|(platform, supported)| {
311 if supported {
312 format!("{}", platform)
313 } else {
314 format!("!{}", platform)
315 }
316 })
317 .collect_vec(),
318 )?,
319 dependencies: PerPlatform::new(project_toml.dependencies.unwrap_or_default()),
322 build_dependencies: PerPlatform::new(
323 project_toml.build_dependencies.unwrap_or_default(),
324 ),
325 external_dependencies: PerPlatform::new(
326 project_toml.external_dependencies.unwrap_or_default(),
327 ),
328 test_dependencies: PerPlatform::new(project_toml.test_dependencies.unwrap_or_default()),
329 test: PerPlatform::new(TestSpec::from_platform_overridable(
330 project_toml.test.clone().unwrap_or_default(),
331 )?),
332 build: PerPlatform::new(BuildSpec::from_internal_spec(project_toml.build.clone())?),
333 deploy: PerPlatform::new(project_toml.deploy.clone().unwrap_or_default()),
334 rockspec_format: project_toml.rockspec_format.clone(),
335
336 source: PerPlatform::new(RemoteRockSource {
337 local: LocalRockSource::default(),
338 source_spec: RockSourceSpec::File(self.project_root.to_path_buf()),
339 }),
340 };
341
342 let rockspec_file_name = format!("{}-{}.rockspec", validated.package, validated.version);
343
344 if validated
345 .build
346 .default
347 .copy_directories
348 .contains(&PathBuf::from(&rockspec_file_name))
349 {
350 return Err(LocalProjectTomlValidationError::CopyDirectoriesContainRockspecName(None));
351 }
352
353 for (platform, build_override) in &validated.build.per_platform {
354 if build_override
355 .copy_directories
356 .contains(&PathBuf::from(&rockspec_file_name))
357 {
358 return Err(
359 LocalProjectTomlValidationError::CopyDirectoriesContainRockspecName(Some(
360 platform.to_string(),
361 )),
362 );
363 }
364 }
365
366 Ok(validated)
367 }
368
369 pub fn into_remote(&self) -> Result<RemoteProjectToml, RemoteProjectTomlValidationError> {
373 let version = self.version_template.try_generate(&self.project_root)?;
374 let source =
375 self.source_template
376 .try_generate(&self.project_root, &self.package, &version)?;
377 let source = PerPlatform::new(
378 RemoteRockSource::from_platform_overridable(source).map_err(|err| {
379 RemoteProjectTomlValidationError::LocalProjectTomlValidationError(
380 LocalProjectTomlValidationError::RockSourceError(err),
381 )
382 })?,
383 );
384 let local = self.into_local()?;
385
386 let validated = RemoteProjectToml { source, local };
387
388 Ok(validated)
389 }
390
391 pub fn package(&self) -> &PackageName {
394 &self.package
395 }
396
397 pub fn version(&self) -> Result<PackageVersion, GenerateVersionError> {
399 self.version_template.try_generate(&self.project_root)
400 }
401
402 pub fn merge(self, other: PartialLuaRockspec) -> Self {
405 PartialProjectToml {
406 package: other.package.unwrap_or(self.package),
407 version_template: self.version_template,
408 lua: other
409 .dependencies
410 .as_ref()
411 .and_then(|deps| {
412 deps.iter()
413 .find(|dep| dep.name() == &"lua".into())
414 .and_then(|dep| {
415 if dep.version_req().is_any() {
416 None
417 } else {
418 Some(dep.version_req().clone())
419 }
420 })
421 })
422 .or(self.lua),
423 build: other.build.unwrap_or(self.build),
424 run: self.run,
425 description: other.description.or(self.description),
426 supported_platforms: other
427 .supported_platforms
428 .map(|platform_support| platform_support.platforms().clone())
429 .or(self.supported_platforms),
430 dependencies: other
431 .dependencies
432 .map(|deps| {
433 deps.into_iter()
434 .filter(|dep| dep.name() != &"lua".into())
435 .collect()
436 })
437 .or(self.dependencies),
438 build_dependencies: other.build_dependencies.or(self.build_dependencies),
439 test_dependencies: other.test_dependencies.or(self.test_dependencies),
440 external_dependencies: other.external_dependencies.or(self.external_dependencies),
441 source_template: self.source_template,
442 test: other.test.or(self.test),
443 deploy: other.deploy.or(self.deploy),
444 rockspec_format: other.rockspec_format.or(self.rockspec_format),
445
446 project_root: self.project_root,
448 }
449 }
450}
451
452impl LuaVersionCompatibility for PartialProjectToml {
456 fn validate_lua_version(&self, config: &Config) -> Result<(), LuaVersionError> {
457 let _ = self.lua_version_matches(config)?;
458 Ok(())
459 }
460
461 fn lua_version_matches(&self, config: &Config) -> Result<LuaVersion, LuaVersionError> {
462 let version = LuaVersion::from(config)?.clone();
463 if self.supports_lua_version(&version) {
464 Ok(version)
465 } else {
466 Err(LuaVersionError::LuaVersionUnsupported(
467 version,
468 self.package.clone(),
469 self.version_template
470 .try_generate(&self.project_root)
471 .unwrap_or(PackageVersion::default_dev_version()),
472 ))
473 }
474 }
475
476 fn supports_lua_version(&self, lua_version: &LuaVersion) -> bool {
477 self.lua
478 .as_ref()
479 .is_none_or(|lua| lua.matches(&lua_version.as_version()))
480 }
481
482 fn lua_version(&self) -> Option<LuaVersion> {
483 for (possibility, version) in [
484 ("5.4.0", LuaVersion::Lua54),
485 ("5.3.0", LuaVersion::Lua53),
486 ("5.2.0", LuaVersion::Lua52),
487 ("5.1.0", LuaVersion::Lua51),
488 ] {
489 if self
490 .lua
491 .as_ref()
492 .is_none_or(|lua| lua.matches(&possibility.parse().unwrap()))
493 {
494 return Some(version);
495 }
496 }
497 None
498 }
499}
500
501#[derive(Debug, Clone, Deserialize)]
503pub struct RunSpec {
504 pub(crate) command: Option<RunCommand>,
506 pub(crate) args: Option<NonEmpty<String>>,
508}
509
510#[derive(Debug)]
514pub struct LocalProjectToml {
515 package: PackageName,
516 version: PackageVersion,
517 lua: PackageVersionReq,
518 rockspec_format: Option<RockspecFormat>,
519 run: Option<PerPlatform<RunSpec>>,
520 description: RockDescription,
521 supported_platforms: PlatformSupport,
522 dependencies: PerPlatform<Vec<LuaDependencySpec>>,
523 build_dependencies: PerPlatform<Vec<LuaDependencySpec>>,
524 external_dependencies: PerPlatform<HashMap<String, ExternalDependencySpec>>,
525 test_dependencies: PerPlatform<Vec<LuaDependencySpec>>,
526 test: PerPlatform<TestSpec>,
527 build: PerPlatform<BuildSpec>,
528 deploy: PerPlatform<DeploySpec>,
529
530 internal: PartialProjectToml,
532
533 source: PerPlatform<RemoteRockSource>,
535}
536
537impl LocalProjectToml {
538 pub fn run(&self) -> Option<&PerPlatform<RunSpec>> {
539 self.run.as_ref()
540 }
541
542 pub fn to_lua_rockspec(&self) -> Result<LocalLuaRockspec, LuaRockspecError> {
545 if let Some(dep) = self
546 .dependencies()
547 .per_platform
548 .iter()
549 .filter_map(|(_, deps)| deps.iter().find(|dep| dep.source().is_some()))
550 .collect_vec()
551 .first()
552 {
553 return Err(LuaRockspecError::OffSpecDependency(dep.name().clone()));
554 }
555 if let Some(dep) = self
556 .build_dependencies()
557 .per_platform
558 .iter()
559 .filter_map(|(_, deps)| deps.iter().find(|dep| dep.source().is_some()))
560 .collect_vec()
561 .first()
562 {
563 return Err(LuaRockspecError::OffSpecBuildDependency(dep.name().clone()));
564 }
565 if let Some(dep) = self
566 .test_dependencies()
567 .per_platform
568 .iter()
569 .filter_map(|(_, deps)| deps.iter().find(|dep| dep.source().is_some()))
570 .collect_vec()
571 .first()
572 {
573 return Err(LuaRockspecError::OffSpecTestDependency(dep.name().clone()));
574 }
575 LocalLuaRockspec::new(
576 &self.to_lua_remote_rockspec_string()?,
577 self.internal.project_root.clone(),
578 )
579 }
580}
581
582impl Rockspec for LocalProjectToml {
583 type Error = ProjectTomlError;
584
585 fn package(&self) -> &PackageName {
586 &self.package
587 }
588
589 fn version(&self) -> &PackageVersion {
590 &self.version
591 }
592
593 fn description(&self) -> &RockDescription {
594 &self.description
595 }
596
597 fn supported_platforms(&self) -> &PlatformSupport {
598 &self.supported_platforms
599 }
600
601 fn lua(&self) -> &PackageVersionReq {
602 &self.lua
603 }
604
605 fn dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
606 &self.dependencies
607 }
608
609 fn build_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
610 &self.build_dependencies
611 }
612
613 fn external_dependencies(&self) -> &PerPlatform<HashMap<String, ExternalDependencySpec>> {
614 &self.external_dependencies
615 }
616
617 fn test_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
618 &self.test_dependencies
619 }
620
621 fn build(&self) -> &PerPlatform<BuildSpec> {
622 &self.build
623 }
624
625 fn test(&self) -> &PerPlatform<TestSpec> {
626 &self.test
627 }
628
629 fn build_mut(&mut self) -> &mut PerPlatform<BuildSpec> {
630 &mut self.build
631 }
632
633 fn test_mut(&mut self) -> &mut PerPlatform<TestSpec> {
634 &mut self.test
635 }
636
637 fn format(&self) -> &Option<RockspecFormat> {
638 &self.rockspec_format
639 }
640
641 fn source(&self) -> &PerPlatform<RemoteRockSource> {
642 &self.source
643 }
644
645 fn source_mut(&mut self) -> &mut PerPlatform<RemoteRockSource> {
646 &mut self.source
647 }
648
649 fn deploy(&self) -> &PerPlatform<DeploySpec> {
650 &self.deploy
651 }
652
653 fn deploy_mut(&mut self) -> &mut PerPlatform<DeploySpec> {
654 &mut self.deploy
655 }
656
657 fn to_lua_remote_rockspec_string(&self) -> Result<String, Self::Error> {
658 let project_root = &self.internal.project_root;
659 let version = self.internal.version_template.try_generate(project_root)?;
660 let starter = format!(
661 r#"
662rockspec_format = "{}"
663package = "{}"
664version = "{}""#,
665 self.rockspec_format.as_ref().unwrap_or(&"3.0".into()),
666 self.package,
667 &version
668 );
669
670 let mut template = Vec::new();
671
672 if self.description != RockDescription::default() {
673 template.push(self.description.display_lua());
674 }
675
676 if self.supported_platforms != PlatformSupport::default() {
677 template.push(self.supported_platforms.display_lua());
678 }
679
680 {
681 let mut dependencies = self.internal.dependencies.clone().unwrap_or_default();
682 dependencies.insert(
683 0,
684 PackageReq {
685 name: "lua".into(),
686 version_req: self.lua.clone(),
687 }
688 .into(),
689 );
690 template.push(Dependencies(&dependencies).display_lua());
691 }
692
693 match self.internal.build_dependencies {
694 Some(ref build_dependencies) if !build_dependencies.is_empty() => {
695 template.push(BuildDependencies(build_dependencies).display_lua());
696 }
697 _ => {}
698 }
699
700 match self.internal.external_dependencies {
701 Some(ref external_dependencies) if !external_dependencies.is_empty() => {
702 template.push(ExternalDependencies(external_dependencies).display_lua());
703 }
704 _ => {}
705 }
706
707 match self.internal.test_dependencies {
708 Some(ref test_dependencies) if !test_dependencies.is_empty() => {
709 template.push(TestDependencies(test_dependencies).display_lua());
710 }
711 _ => {}
712 }
713
714 let source =
715 self.internal
716 .source_template
717 .try_generate(project_root, &self.package, &version)?;
718 template.push(source.display_lua());
719
720 if let Some(ref test) = self.internal.test {
721 template.push(test.display_lua());
722 }
723
724 template.push(self.internal.build.display_lua());
725
726 Ok(std::iter::once(starter)
727 .chain(template.into_iter().map(|kv| kv.to_string()))
728 .join("\n\n"))
729 }
730}
731
732#[derive(Error, Debug)]
733#[error(transparent)]
734pub enum ProjectTomlIntegrityError {
735 LuaRockspecError(#[from] LuaRockspecError),
736 IoError(#[from] io::Error),
737}
738
739impl HasIntegrity for LocalProjectToml {
740 fn hash(&self) -> io::Result<Integrity> {
741 match self.to_lua_rockspec() {
742 Ok(lua_rockspec) => lua_rockspec.hash(),
743 Err(_) => self.internal.hash(),
744 }
745 }
746}
747
748#[derive(Debug)]
749pub struct RemoteProjectToml {
750 local: LocalProjectToml,
751 source: PerPlatform<RemoteRockSource>,
752}
753
754impl RemoteProjectToml {
755 pub fn to_lua_rockspec(&self) -> Result<RemoteLuaRockspec, LuaRockspecError> {
756 RemoteLuaRockspec::new(&self.to_lua_remote_rockspec_string()?)
757 }
758}
759
760impl Rockspec for RemoteProjectToml {
761 type Error = ProjectTomlError;
762
763 fn package(&self) -> &PackageName {
764 self.local.package()
765 }
766
767 fn version(&self) -> &PackageVersion {
768 self.local.version()
769 }
770
771 fn description(&self) -> &RockDescription {
772 self.local.description()
773 }
774
775 fn supported_platforms(&self) -> &PlatformSupport {
776 self.local.supported_platforms()
777 }
778
779 fn lua(&self) -> &PackageVersionReq {
780 self.local.lua()
781 }
782
783 fn dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
784 self.local.dependencies()
785 }
786
787 fn build_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
788 self.local.build_dependencies()
789 }
790
791 fn external_dependencies(&self) -> &PerPlatform<HashMap<String, ExternalDependencySpec>> {
792 self.local.external_dependencies()
793 }
794
795 fn test_dependencies(&self) -> &PerPlatform<Vec<LuaDependencySpec>> {
796 self.local.test_dependencies()
797 }
798
799 fn build(&self) -> &PerPlatform<BuildSpec> {
800 self.local.build()
801 }
802
803 fn test(&self) -> &PerPlatform<TestSpec> {
804 self.local.test()
805 }
806
807 fn build_mut(&mut self) -> &mut PerPlatform<BuildSpec> {
808 self.local.build_mut()
809 }
810
811 fn test_mut(&mut self) -> &mut PerPlatform<TestSpec> {
812 self.local.test_mut()
813 }
814
815 fn format(&self) -> &Option<RockspecFormat> {
816 self.local.format()
817 }
818
819 fn source(&self) -> &PerPlatform<RemoteRockSource> {
820 &self.source
821 }
822
823 fn source_mut(&mut self) -> &mut PerPlatform<RemoteRockSource> {
824 &mut self.source
825 }
826
827 fn deploy(&self) -> &PerPlatform<DeploySpec> {
828 self.local.deploy()
829 }
830
831 fn deploy_mut(&mut self) -> &mut PerPlatform<DeploySpec> {
832 self.local.deploy_mut()
833 }
834
835 fn to_lua_remote_rockspec_string(&self) -> Result<String, Self::Error> {
836 let project_root = &self.local.internal.project_root;
837 let version = self
838 .local
839 .internal
840 .version_template
841 .try_generate(project_root)?;
842
843 let starter = format!(
844 r#"
845rockspec_format = "{}"
846package = "{}"
847version = "{}""#,
848 self.local.rockspec_format.as_ref().unwrap_or(&"3.0".into()),
849 self.local.package,
850 &version
851 );
852
853 let mut template = Vec::new();
854
855 if self.local.description != RockDescription::default() {
856 template.push(self.local.description.display_lua());
857 }
858
859 if self.local.supported_platforms != PlatformSupport::default() {
860 template.push(self.local.supported_platforms.display_lua());
861 }
862
863 {
864 let mut dependencies = self.local.internal.dependencies.clone().unwrap_or_default();
865 dependencies.insert(
866 0,
867 PackageReq {
868 name: "lua".into(),
869 version_req: self.local.lua.clone(),
870 }
871 .into(),
872 );
873 template.push(Dependencies(&dependencies).display_lua());
874 }
875
876 match self.local.internal.build_dependencies {
877 Some(ref build_dependencies) if !build_dependencies.is_empty() => {
878 template.push(BuildDependencies(build_dependencies).display_lua());
879 }
880 _ => {}
881 }
882
883 match self.local.internal.external_dependencies {
884 Some(ref external_dependencies) if !external_dependencies.is_empty() => {
885 template.push(ExternalDependencies(external_dependencies).display_lua());
886 }
887 _ => {}
888 }
889
890 match self.local.internal.test_dependencies {
891 Some(ref test_dependencies) if !test_dependencies.is_empty() => {
892 template.push(TestDependencies(test_dependencies).display_lua());
893 }
894 _ => {}
895 }
896
897 let source = self.local.internal.source_template.try_generate(
898 project_root,
899 &self.local.internal.package,
900 &version,
901 )?;
902 template.push(source.display_lua());
903
904 if let Some(ref test) = self.local.internal.test {
905 template.push(test.display_lua());
906 }
907
908 template.push(self.local.internal.build.display_lua());
909
910 Ok(std::iter::once(starter)
911 .chain(template.into_iter().map(|kv| kv.to_string()))
912 .join("\n\n"))
913 }
914}
915
916impl HasIntegrity for RemoteProjectToml {
917 fn hash(&self) -> io::Result<Integrity> {
918 self.to_lua_rockspec()
919 .expect("unable to convert remote project to rockspec")
920 .hash()
921 }
922}
923
924impl UserData for LocalProjectToml {
925 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
926 methods.add_method("package", |_, this, _: ()| Ok(this.package().clone()));
927 methods.add_method("version", |_, this, _: ()| Ok(this.version().clone()));
928 methods.add_method("description", |_, this, _: ()| {
929 Ok(this.description().clone())
930 });
931 methods.add_method("supported_platforms", |_, this, _: ()| {
932 Ok(this.supported_platforms().clone())
933 });
934 methods.add_method("dependencies", |_, this, _: ()| {
935 Ok(this.dependencies().clone())
936 });
937 methods.add_method("build_dependencies", |_, this, _: ()| {
938 Ok(this.build_dependencies().clone())
939 });
940 methods.add_method("external_dependencies", |_, this, _: ()| {
941 Ok(this.external_dependencies().clone())
942 });
943 methods.add_method("test_dependencies", |_, this, _: ()| {
944 Ok(this.test_dependencies().clone())
945 });
946 methods.add_method("build", |_, this, _: ()| Ok(this.build().clone()));
947 methods.add_method("test", |_, this, _: ()| Ok(this.test().clone()));
948 methods.add_method("format", |_, this, _: ()| Ok(this.format().clone()));
949 methods.add_method("source", |_, this, _: ()| Ok(this.source().clone()));
950 methods.add_method("to_lua_rockspec_string", |_, this, _: ()| {
951 this.to_lua_remote_rockspec_string()
952 .map_err(|err| mlua::Error::RuntimeError(err.to_string()))
953 });
954 methods.add_method("to_lua_rockspec", |_, this, _: ()| {
955 this.to_lua_rockspec().into_lua_err()
956 });
957 }
958}
959
960impl UserData for RemoteProjectToml {
961 fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
962 methods.add_method("package", |_, this, _: ()| Ok(this.package().clone()));
963 methods.add_method("version", |_, this, _: ()| Ok(this.version().clone()));
964 methods.add_method("description", |_, this, _: ()| {
965 Ok(this.description().clone())
966 });
967 methods.add_method("supported_platforms", |_, this, _: ()| {
968 Ok(this.supported_platforms().clone())
969 });
970 methods.add_method("dependencies", |_, this, _: ()| {
971 Ok(this.dependencies().clone())
972 });
973 methods.add_method("build_dependencies", |_, this, _: ()| {
974 Ok(this.build_dependencies().clone())
975 });
976 methods.add_method("external_dependencies", |_, this, _: ()| {
977 Ok(this.external_dependencies().clone())
978 });
979 methods.add_method("test_dependencies", |_, this, _: ()| {
980 Ok(this.test_dependencies().clone())
981 });
982 methods.add_method("build", |_, this, _: ()| Ok(this.build().clone()));
983 methods.add_method("test", |_, this, _: ()| Ok(this.test().clone()));
984 methods.add_method("format", |_, this, _: ()| Ok(this.format().clone()));
985 methods.add_method("source", |_, this, _: ()| Ok(this.source().clone()));
986 methods.add_method("to_lua_rockspec_string", |_, this, _: ()| {
987 this.to_lua_remote_rockspec_string()
988 .map_err(|err| mlua::Error::RuntimeError(err.to_string()))
989 });
990 methods.add_method("to_lua_rockspec", |_, this, _: ()| {
991 this.to_lua_rockspec().into_lua_err()
992 });
993 }
994}
995
996#[cfg(test)]
997mod tests {
998 use std::path::PathBuf;
999
1000 use assert_fs::prelude::PathCopy;
1001 use git2::{Repository, RepositoryInitOptions};
1002 use git_url_parse::GitUrl;
1003 use url::Url;
1004
1005 use crate::{
1006 git::GitSource,
1007 lua_rockspec::{PartialLuaRockspec, PerPlatform, RemoteLuaRockspec, RockSourceSpec},
1008 project::{Project, ProjectRoot},
1009 rockspec::{lua_dependency::LuaDependencySpec, Rockspec},
1010 };
1011
1012 use super::PartialProjectToml;
1013
1014 #[test]
1015 fn project_toml_parsing() {
1016 let project_toml = r#"
1017 package = "my-package"
1018 version = "1.0.0"
1019 lua = "5.3"
1020
1021 rockspec_format = "1.0"
1022
1023 [source]
1024 url = "https://example.com"
1025
1026 [dependencies]
1027 foo = "1.0"
1028 bar = ">=2.0"
1029
1030 [run]
1031 args = ["--foo", "--bar"]
1032
1033 [build]
1034 type = "builtin"
1035 "#;
1036
1037 let project = PartialProjectToml::new(project_toml, ProjectRoot::default()).unwrap();
1038 let _ = project.into_remote().unwrap();
1039
1040 let project_toml = r#"
1041 package = "my-package"
1042 version = "1.0.0"
1043 lua = "5.1"
1044
1045 [description]
1046 summary = "A summary"
1047 detailed = "A detailed description"
1048 license = "MIT"
1049 homepage = "https://example.com"
1050 issues_url = "https://example.com/issues"
1051 maintainer = "John Doe"
1052 labels = ["label1", "label2"]
1053
1054 [supported_platforms]
1055 linux = true
1056 windows = false
1057
1058 [dependencies]
1059 foo = "1.0"
1060 bar = ">=2.0"
1061
1062 [build_dependencies]
1063 baz = "1.0"
1064
1065 [external_dependencies.foo]
1066 header = "foo.h"
1067
1068 [external_dependencies.bar]
1069 library = "libbar.so"
1070
1071 [test_dependencies]
1072 busted = "69.420"
1073
1074 [source]
1075 url = "https://example.com"
1076 hash = "sha256-di00mD8txN7rjaVpvxzNbnQsAh6H16zUtJZapH7U4HU="
1077 file = "my-package-1.0.0.tar.gz"
1078 dir = "my-package-1.0.0"
1079
1080 [test]
1081 type = "command"
1082 script = "test.lua"
1083 flags = [ "foo", "bar" ]
1084
1085 [run]
1086 command = "my-command"
1087 args = ["--foo", "--bar"]
1088
1089 [build]
1090 type = "builtin"
1091 "#;
1092
1093 let project = PartialProjectToml::new(project_toml, ProjectRoot::default()).unwrap();
1094 let _ = project.into_remote().unwrap();
1095 }
1096
1097 #[test]
1098 fn compare_project_toml_with_rockspec() {
1099 let project_toml = r#"
1100 package = "my-package"
1101 version = "1.0.0"
1102 lua = "5.1"
1103
1104 # For testing, specify a custom rockspec format
1105 # (defaults to 3.0)
1106 rockspec_format = "1.0"
1107
1108 [description]
1109 summary = "A summary"
1110 detailed = "A detailed description"
1111 license = "MIT"
1112 homepage = "https://example.com"
1113 issues_url = "https://example.com/issues"
1114 maintainer = "John Doe"
1115 labels = ["label1", "label2"]
1116
1117 [supported_platforms]
1118 linux = true
1119 windows = false
1120
1121 [dependencies]
1122 foo = "1.0"
1123 bar = ">=2.0"
1124
1125 [build_dependencies]
1126 baz = "1.0"
1127
1128 [external_dependencies.foo]
1129 header = "foo.h"
1130
1131 [external_dependencies.bar]
1132 library = "libbar.so"
1133
1134 [test_dependencies]
1135 busted = "1.0"
1136
1137 [source]
1138 url = "https://example.com"
1139 file = "my-package-1.0.0.tar.gz"
1140 dir = "my-package-1.0.0"
1141
1142 [test]
1143 type = "command"
1144 script = "test.lua"
1145 flags = [ "foo", "bar" ]
1146
1147 [run]
1148 command = "my-command"
1149 args = ["--foo", "--bar"]
1150
1151 [build]
1152 type = "builtin"
1153 "#;
1154
1155 let expected_rockspec = r#"
1156 rockspec_format = "1.0"
1157 package = "my-package"
1158 version = "1.0.0"
1159
1160 source = {
1161 url = "https://example.com",
1162 file = "my-package-1.0.0.tar.gz",
1163 dir = "my-package-1.0.0",
1164 }
1165
1166 description = {
1167 summary = "A summary",
1168 detailed = "A detailed description",
1169 license = "MIT",
1170 homepage = "https://example.com",
1171 issues_url = "https://example.com/issues",
1172 maintainer = "John Doe",
1173 labels = {"label1", "label2"},
1174 }
1175
1176 supported_platforms = {"linux", "!windows"}
1177
1178 dependencies = {
1179 "lua ==5.1",
1180 "foo ==1.0",
1181 "bar >=2.0",
1182 }
1183
1184 build_dependencies = {
1185 "baz ==1.0",
1186 }
1187
1188 external_dependencies = {
1189 foo = { header = "foo.h" },
1190 bar = { library = "libbar.so" },
1191 }
1192
1193 test_dependencies = {
1194 "busted ==1.0",
1195 }
1196
1197 source = {
1198 url = "https://example.com",
1199 hash = "sha256-di00mD8txN7rjaVpvxzNbnQsAh6H16zUtJZapH7U4HU=",
1200 file = "my-package-1.0.0.tar.gz",
1201 dir = "my-package-1.0.0",
1202 }
1203
1204 test = {
1205 type = "command",
1206 script = "test.lua",
1207 flags = {"foo", "bar"},
1208 }
1209
1210 build = {
1211 type = "builtin",
1212 }
1213 "#;
1214
1215 let expected_rockspec = RemoteLuaRockspec::new(expected_rockspec).unwrap();
1216
1217 let project_toml = PartialProjectToml::new(project_toml, ProjectRoot::default()).unwrap();
1218 let rockspec = project_toml
1219 .into_remote()
1220 .unwrap()
1221 .to_lua_rockspec()
1222 .unwrap();
1223
1224 let sorted_package_reqs = |v: &PerPlatform<Vec<LuaDependencySpec>>| {
1225 let mut v = v.current_platform().clone();
1226 v.sort_by(|a, b| a.name().cmp(b.name()));
1227 v
1228 };
1229
1230 assert_eq!(rockspec.package(), expected_rockspec.package());
1231 assert_eq!(rockspec.version(), expected_rockspec.version());
1232 assert_eq!(rockspec.description(), expected_rockspec.description());
1233 assert_eq!(
1234 rockspec.supported_platforms(),
1235 expected_rockspec.supported_platforms()
1236 );
1237 assert_eq!(
1238 sorted_package_reqs(rockspec.dependencies()),
1239 sorted_package_reqs(expected_rockspec.dependencies())
1240 );
1241 assert_eq!(
1242 sorted_package_reqs(rockspec.build_dependencies()),
1243 sorted_package_reqs(expected_rockspec.build_dependencies())
1244 );
1245 assert_eq!(
1246 rockspec.external_dependencies(),
1247 expected_rockspec.external_dependencies()
1248 );
1249 assert_eq!(
1250 sorted_package_reqs(rockspec.test_dependencies()),
1251 sorted_package_reqs(expected_rockspec.test_dependencies())
1252 );
1253 assert_eq!(rockspec.source(), expected_rockspec.source());
1254 assert_eq!(rockspec.test(), expected_rockspec.test());
1255 assert_eq!(rockspec.build(), expected_rockspec.build());
1256 assert_eq!(rockspec.format(), expected_rockspec.format());
1257 }
1258
1259 #[test]
1260 fn merge_project_toml_with_partial_rockspec() {
1261 let project_toml = r#"
1262 package = "my-package"
1263 version = "1.0.0"
1264 lua = "5.1"
1265
1266 # For testing, specify a custom rockspec format
1267 # (defaults to 3.0)
1268 rockspec_format = "1.0"
1269
1270 [description]
1271 summary = "A summary"
1272 detailed = "A detailed description"
1273 license = "MIT"
1274 homepage = "https://example.com"
1275 issues_url = "https://example.com/issues"
1276 maintainer = "John Doe"
1277 labels = ["label1", "label2"]
1278
1279 [supported_platforms]
1280 linux = true
1281 windows = false
1282
1283 [dependencies]
1284 foo = "1.0"
1285 bar = ">=2.0"
1286
1287 [build_dependencies]
1288 baz = "1.0"
1289
1290 [external_dependencies.foo]
1291 header = "foo.h"
1292
1293 [external_dependencies.bar]
1294 library = "libbar.so"
1295
1296 [test_dependencies]
1297 busted = "1.0"
1298
1299 [source]
1300 url = "https://example.com"
1301 file = "my-package-1.0.0.tar.gz"
1302 dir = "my-package-1.0.0"
1303
1304 [test]
1305 type = "command"
1306 script = "test.lua"
1307 flags = [ "foo", "bar" ]
1308
1309 [run]
1310 command = "my-command"
1311 args = [ "--foo", "--bar" ]
1312
1313 [build]
1314 type = "builtin"
1315 "#;
1316
1317 let mergable_rockspec_content = r#"
1318 rockspec_format = "1.0"
1319 package = "my-package-overwritten"
1320
1321 description = {
1322 summary = "A summary overwritten",
1323 detailed = "A detailed description overwritten",
1324 license = "GPL-2.0",
1325 homepage = "https://example.com/overwritten",
1326 issues_url = "https://example.com/issues/overwritten",
1327 maintainer = "John Doe Overwritten",
1328 labels = {"over", "written"},
1329 }
1330
1331 -- Inverted supported platforms
1332 supported_platforms = {"!linux", "windows"}
1333
1334 dependencies = {
1335 "lua 5.1",
1336 "foo >1.0",
1337 "bar <=2.0",
1338 }
1339
1340 build_dependencies = {
1341 "baz >1.0",
1342 }
1343
1344 external_dependencies = {
1345 foo = { header = "overwritten.h" },
1346 bar = { library = "overwritten.so" },
1347 }
1348
1349 test_dependencies = {
1350 "busted >1.0",
1351 }
1352
1353 test = {
1354 type = "command",
1355 script = "overwritten.lua",
1356 flags = {"over", "written"},
1357 }
1358
1359 build = {
1360 type = "builtin",
1361 }
1362 "#;
1363
1364 let remote_rockspec_content = format!(
1365 r#"{}
1366 version = "1.0.0"
1367 source = {{
1368 url = "https://example.com",
1369 file = "my-package-1.0.0.tar.gz",
1370 dir = "my-package-1.0.0",
1371 }}
1372 "#,
1373 &mergable_rockspec_content
1374 );
1375
1376 let project_toml = PartialProjectToml::new(project_toml, ProjectRoot::default()).unwrap();
1377 let partial_rockspec = PartialLuaRockspec::new(mergable_rockspec_content).unwrap();
1378 let expected_rockspec = RemoteLuaRockspec::new(&remote_rockspec_content).unwrap();
1379
1380 let merged = project_toml.merge(partial_rockspec).into_remote().unwrap();
1381
1382 let sorted_package_reqs = |v: &PerPlatform<Vec<LuaDependencySpec>>| {
1383 let mut v = v.current_platform().clone();
1384 v.sort_by(|a, b| a.name().cmp(b.name()));
1385 v
1386 };
1387
1388 assert_eq!(merged.package(), expected_rockspec.package());
1389 assert_eq!(merged.version(), expected_rockspec.version());
1390 assert_eq!(merged.description(), expected_rockspec.description());
1391 assert_eq!(
1392 merged.supported_platforms(),
1393 expected_rockspec.supported_platforms()
1394 );
1395 assert_eq!(
1396 sorted_package_reqs(merged.dependencies()),
1397 sorted_package_reqs(expected_rockspec.dependencies())
1398 );
1399 assert_eq!(
1400 sorted_package_reqs(merged.build_dependencies()),
1401 sorted_package_reqs(expected_rockspec.build_dependencies())
1402 );
1403 assert_eq!(
1404 merged.external_dependencies(),
1405 expected_rockspec.external_dependencies()
1406 );
1407 assert_eq!(
1408 sorted_package_reqs(merged.test_dependencies()),
1409 sorted_package_reqs(expected_rockspec.test_dependencies())
1410 );
1411 assert_eq!(merged.source(), expected_rockspec.source());
1412 assert_eq!(merged.test(), expected_rockspec.test());
1413 assert_eq!(merged.build(), expected_rockspec.build());
1414 assert_eq!(merged.format(), expected_rockspec.format());
1415 assert!(merged.local.run().is_some());
1417 }
1418
1419 #[test]
1420 fn project_toml_with_lua_in_dependencies() {
1421 let project_toml = r#"
1422 package = "my-package"
1423 version = "1.0.0"
1424 # lua = ">5.1"
1425
1426 [dependencies]
1427 lua = "5.1" # disallowed
1428
1429 [build]
1430 type = "builtin"
1431 "#;
1432
1433 PartialProjectToml::new(project_toml, ProjectRoot::default())
1434 .unwrap()
1435 .into_local()
1436 .unwrap_err();
1437 }
1438
1439 #[test]
1440 fn project_toml_with_invalid_run_command() {
1441 for command in ["lua", "lua5.1", "lua5.2", "lua5.3", "lua5.4", "luajit"] {
1442 let project_toml = format!(
1443 r#"
1444 package = "my-package"
1445 version = "1.0.0"
1446 lua = "5.1"
1447
1448 [build]
1449 type = "builtin"
1450
1451 [run]
1452 command = "{command}"
1453 "#,
1454 );
1455
1456 PartialProjectToml::new(&project_toml, ProjectRoot::default()).unwrap_err();
1457 }
1458 }
1459
1460 #[test]
1461 fn generate_non_deterministic_git_source() {
1462 let rockspec_content = r#"
1463 package = "test-package"
1464 version = "1.0.0"
1465 lua = ">=5.1"
1466
1467 [source]
1468 url = "git+https://exaple.com/repo.git"
1469
1470 [build]
1471 type = "builtin"
1472 "#;
1473
1474 PartialProjectToml::new(rockspec_content, ProjectRoot::default())
1475 .unwrap()
1476 .into_remote()
1477 .unwrap_err();
1478 }
1479
1480 #[test]
1481 fn generate_deterministic_git_source() {
1482 let rockspec_content = r#"
1483 package = "test-package"
1484 version = "1.0.0"
1485 lua = ">=5.1"
1486
1487 [source]
1488 url = "git+https://exaple.com/repo.git"
1489 tag = "v0.1.0"
1490
1491 [build]
1492 type = "builtin"
1493 "#;
1494
1495 PartialProjectToml::new(rockspec_content, ProjectRoot::default())
1496 .unwrap()
1497 .into_remote()
1498 .unwrap();
1499 }
1500
1501 fn init_sample_project_repo(temp_dir: &assert_fs::TempDir) -> Repository {
1502 let sample_project: PathBuf = "resources/test/sample-project-source-template/".into();
1503 temp_dir.copy_from(&sample_project, &["**"]).unwrap();
1504 let repo = Repository::init(temp_dir).unwrap();
1505 let mut opts = RepositoryInitOptions::new();
1506 opts.initial_head("main");
1507 {
1508 let mut config = repo.config().unwrap();
1509 config.set_str("user.name", "name").unwrap();
1510 config.set_str("user.email", "email").unwrap();
1511 let mut index = repo.index().unwrap();
1512 let id = index.write_tree().unwrap();
1513
1514 let tree = repo.find_tree(id).unwrap();
1515 let sig = repo.signature().unwrap();
1516 repo.commit(Some("HEAD"), &sig, &sig, "initial\n\nbody", &tree, &[])
1517 .unwrap();
1518 }
1519 repo
1520 }
1521
1522 fn create_tag(repo: &Repository, name: &str) {
1523 let sig = repo.signature().unwrap();
1524 let id = repo.head().unwrap().target().unwrap();
1525 let obj = repo.find_object(id, None).unwrap();
1526 repo.tag(name, &obj, &sig, "msg", true).unwrap();
1527 }
1528
1529 #[test]
1530 fn test_git_project_generate_dev_source() {
1531 let project_root = assert_fs::TempDir::new().unwrap();
1532 init_sample_project_repo(&project_root);
1533 let project = Project::from(&project_root).unwrap().unwrap();
1534 let remote_project_toml = project.toml().into_remote().unwrap();
1535 let source_spec = &remote_project_toml.source.current_platform().source_spec;
1536 assert!(matches!(source_spec, &RockSourceSpec::Git { .. }));
1537 if let RockSourceSpec::Git(GitSource { url, checkout_ref }) = source_spec {
1538 let expected_url: GitUrl = "https://github.com/nvim-neorocks/lux.git".parse().unwrap();
1539 assert_eq!(url, &expected_url);
1540 assert!(checkout_ref.is_some());
1541 }
1542 }
1543
1544 #[test]
1545 fn test_git_project_generate_non_semver_tag_source() {
1546 let project_root = assert_fs::TempDir::new().unwrap();
1547 let repo = init_sample_project_repo(&project_root);
1548 let tag_name = "bla";
1549 create_tag(&repo, tag_name);
1550 let project = Project::from(&project_root).unwrap().unwrap();
1551 let remote_project_toml = project.toml().into_remote().unwrap();
1552 let source_spec = &remote_project_toml.source.current_platform().source_spec;
1553 assert!(matches!(source_spec, &RockSourceSpec::Git { .. }));
1554 if let RockSourceSpec::Git(GitSource { url, checkout_ref }) = source_spec {
1555 let expected_url: GitUrl = "https://github.com/nvim-neorocks/lux.git".parse().unwrap();
1556 assert_eq!(url, &expected_url);
1557 assert_eq!(checkout_ref, &Some(tag_name.to_string()));
1558 }
1559 }
1560
1561 #[test]
1562 fn test_git_project_generate_release_source_tag_with_v_prefix() {
1563 let project_root = assert_fs::TempDir::new().unwrap();
1564 let repo = init_sample_project_repo(&project_root);
1565 let tag_name = "v1.0.0";
1566 create_tag(&repo, "bla");
1567 create_tag(&repo, tag_name);
1568 let project = Project::from(&project_root).unwrap().unwrap();
1569 let remote_project_toml = project.toml().into_remote().unwrap();
1570 let source_spec = &remote_project_toml.source.current_platform().source_spec;
1571 assert!(matches!(source_spec, &RockSourceSpec::Url { .. }));
1572 if let RockSourceSpec::Url(url) = source_spec {
1573 let expected_url: Url =
1574 "https://github.com/nvim-neorocks/lux/archive/refs/tags/v1.0.0.zip"
1575 .parse()
1576 .unwrap();
1577 assert_eq!(url, &expected_url);
1578 }
1579 }
1580
1581 #[test]
1582 fn test_git_project_generate_release_source_tag_without_v_prefix() {
1583 let project_root = assert_fs::TempDir::new().unwrap();
1584 let repo = init_sample_project_repo(&project_root);
1585 create_tag(&repo, "bla");
1586 let tag_name = "1.0.0";
1587 create_tag(&repo, tag_name);
1588 let project = Project::from(&project_root).unwrap().unwrap();
1589 let remote_project_toml = project.toml().into_remote().unwrap();
1590 let source_spec = &remote_project_toml.source.current_platform().source_spec;
1591 assert!(matches!(source_spec, &RockSourceSpec::Url { .. }));
1592 if let RockSourceSpec::Url(url) = source_spec {
1593 let expected_url: Url =
1594 "https://github.com/nvim-neorocks/lux/archive/refs/tags/1.0.0.zip"
1595 .parse()
1596 .unwrap();
1597 assert_eq!(url, &expected_url);
1598 }
1599 }
1600}