1use std::{path::PathBuf, str::FromStr};
2
3use clap::Args;
4use eyre::{eyre, Result};
5use lux_lib::{
6 build::{Build, BuildBehaviour},
7 config::{Config, LuaVersion},
8 lua_rockspec::RemoteLuaRockspec,
9 operations::{self, Install, PackageInstallSpec},
10 package::PackageReq,
11 progress::MultiProgress,
12 project::Project,
13 tree,
14};
15use tempdir::TempDir;
16
17#[derive(Debug, Clone)]
18pub enum PackageOrRockspec {
19 Package(PackageReq),
20 RockSpec(PathBuf),
21}
22
23impl FromStr for PackageOrRockspec {
24 type Err = eyre::Error;
25
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 let path = PathBuf::from(s);
28 if path.is_file() {
29 Ok(Self::RockSpec(path))
30 } else {
31 let pkg = PackageReq::from_str(s).map_err(|err| {
32 eyre!(
33 "No file {0} found and cannot parse package query: {1}",
34 s,
35 err
36 )
37 })?;
38 Ok(Self::Package(pkg))
39 }
40 }
41}
42
43#[derive(Args)]
44pub struct Pack {
45 #[clap(value_parser)]
53 package_or_rockspec: Option<PackageOrRockspec>,
54}
55
56pub async fn pack(args: Pack, config: Config) -> Result<()> {
57 let lua_version = LuaVersion::from(&config)?.clone();
58 let dest_dir = std::env::current_dir()?;
59 let progress = MultiProgress::new_arc();
60 let package_or_rockspec = match args.package_or_rockspec {
61 Some(package_or_rockspec) => package_or_rockspec,
62 None => {
63 let project = Project::current_or_err()?;
64 PackageOrRockspec::RockSpec(project.toml_path())
65 }
66 };
67 let result: Result<PathBuf> = match package_or_rockspec {
68 PackageOrRockspec::Package(package_req) => {
69 let user_tree = config.user_tree(lua_version.clone())?;
70 match user_tree.match_rocks(&package_req)? {
71 lux_lib::tree::RockMatches::NotFound(_) => {
72 let temp_dir = TempDir::new("lux-pack")?.into_path();
73 let temp_config = config.with_tree(temp_dir);
74 let tree = temp_config.user_tree(lua_version.clone())?;
75 let packages = Install::new(&temp_config)
76 .package(
77 PackageInstallSpec::new(package_req, tree::EntryType::Entrypoint)
78 .build_behaviour(BuildBehaviour::Force)
79 .build(),
80 )
81 .tree(tree.clone())
82 .progress(progress)
83 .install()
84 .await?;
85 let package = packages.first().unwrap();
86 let rock_path = operations::Pack::new(dest_dir, tree, package.clone())
87 .pack()
88 .await?;
89 Ok(rock_path)
90 }
91 lux_lib::tree::RockMatches::Single(local_package_id) => {
92 let lockfile = user_tree.lockfile()?;
93 let package = lockfile.get(&local_package_id).unwrap();
94 let rock_path = operations::Pack::new(dest_dir, user_tree, package.clone())
95 .pack()
96 .await?;
97 Ok(rock_path)
98 }
99 lux_lib::tree::RockMatches::Many(vec) => {
100 let local_package_id = vec.first().unwrap();
101 let lockfile = user_tree.lockfile()?;
102 let package = lockfile.get(local_package_id).unwrap();
103 let rock_path = operations::Pack::new(dest_dir, user_tree, package.clone())
104 .pack()
105 .await?;
106 Ok(rock_path)
107 }
108 }
109 }
110 PackageOrRockspec::RockSpec(rockspec_path) => {
111 let content = std::fs::read_to_string(&rockspec_path)?;
112 let rockspec = match rockspec_path
113 .extension()
114 .map(|ext| ext.to_string_lossy().to_string())
115 .unwrap_or("".into())
116 .as_str()
117 {
118 ".rockspec" => Ok(RemoteLuaRockspec::new(&content)?),
119 _ => Err(eyre!(
120 "expected a path to a .rockspec or a package requirement."
121 )),
122 }?;
123 let temp_dir = TempDir::new("lux-pack")?.into_path();
124 let bar = progress.map(|p| p.new_bar());
125 let config = config.with_tree(temp_dir);
126 let tree = config.user_tree(lua_version)?;
127 let package = Build::new(&rockspec, &tree, tree::EntryType::Entrypoint, &config, &bar)
128 .build()
129 .await?;
130 let rock_path = operations::Pack::new(dest_dir, tree, package)
131 .pack()
132 .await?;
133 Ok(rock_path)
134 }
135 };
136 print!("packed rock created at {}", result?.display());
137 Ok(())
138}