| 642da263 Andy McCall ↶ |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
use super::json; |
| 6 |
use super::open; |
| 7 |
use super::ui; |
| 8 |
use anyhow::Result; |
| 9 |
use cylinder::{Kind, Spec}; |
| 10 |
use std::path::Path; |
| 11 |
|
| 12 |
|
| 13 |
fn pairs(list: &[String], what: &str) -> Result<std::collections::BTreeMap<String, String>> { |
| 14 |
list.iter() |
| 15 |
.map(|p| { |
| 16 |
p.split_once('=') |
| 17 |
.map(|(k, v)| (k.to_string(), v.to_string())) |
| 18 |
.ok_or_else(|| anyhow::anyhow!("{what} {p:?}: write it KEY=VALUE")) |
| 19 |
}) |
| 20 |
.collect() |
| 21 |
} |
| 22 |
|
| 23 |
fn summary(k: &cylinder::Kept) -> String { |
| 24 |
let a = &k.artifact; |
| 25 |
let mut parts = vec![a.kind.as_str().to_string()]; |
| 26 |
parts.extend(a.os.clone()); |
| 27 |
parts.extend(a.arch.clone()); |
| 28 |
format!( |
| 29 |
"{} {} {}", |
| 30 |
ui::hash(&k.name[..12]), |
| 31 |
parts.join(" "), |
| 32 |
ui::dim(format!("{} bytes", a.total_size)) |
| 33 |
) |
| 34 |
} |
| 35 |
|
| 36 |
fn as_json(k: &cylinder::Kept, files: usize) -> String { |
| 37 |
let a = &k.artifact; |
| 38 |
let text = |v: &Option<String>| v.as_deref().map(json::s).unwrap_or_else(|| "null".into()); |
| 39 |
let map = |m: &std::collections::BTreeMap<String, String>| { |
| 40 |
let inner: Vec<String> = m |
| 41 |
.iter() |
| 42 |
.map(|(k, v)| format!("{}:{}", json::s(k), json::s(v))) |
| 43 |
.collect(); |
| 44 |
format!("{{{}}}", inner.join(",")) |
| 45 |
}; |
| 46 |
json::obj([ |
| 47 |
("name", json::s(&k.name)), |
| 48 |
("kind", json::s(a.kind.as_str())), |
| 49 |
("arch", text(&a.arch)), |
| 50 |
("os", text(&a.os)), |
| 51 |
("tree", json::s(&a.tree.to_hex())), |
| 52 |
("files", json::n(files as u64)), |
| 53 |
("total_size", json::n(a.total_size)), |
| 54 |
("env", map(&a.env)), |
| 55 |
("labels", map(&a.labels)), |
| 56 |
]) |
| 57 |
} |
| 58 |
|
| 59 |
#[allow(clippy::too_many_arguments)] |
| 60 |
pub fn pack( |
| 61 |
dir: &Path, |
| 62 |
kind: &str, |
| 63 |
arch: Option<String>, |
| 64 |
os: Option<String>, |
| 65 |
entrypoint: Vec<String>, |
| 66 |
env: &[String], |
| 67 |
labels: &[String], |
| 68 |
as_json_out: bool, |
| 69 |
) -> Result<i32> { |
| 70 |
let repo = open()?; |
| 71 |
let mut spec = Spec::new(Kind::parse(kind)?); |
| 72 |
spec.arch = arch; |
| 73 |
spec.os = os; |
| 74 |
spec.entrypoint = (!entrypoint.is_empty()).then_some(entrypoint); |
| 75 |
spec.env = pairs(env, "env")?; |
| 76 |
spec.labels = pairs(labels, "label")?; |
| 77 |
let sp = ui::spinner("Packing…"); |
| 78 |
let k = cylinder::pack(&repo, dir, &spec)?; |
| 79 |
sp.clear(); |
| 80 |
let files = cylinder::tree(&repo, &k.artifact)?.entries.len(); |
| 81 |
if as_json_out { |
| 82 |
println!("{}", as_json(&k, files)); |
| 83 |
} else { |
| 84 |
println!( |
| 85 |
"{} {} as {}", |
| 86 |
ui::done("Packed"), |
| 87 |
ui::path(dir.display()), |
| 88 |
summary(&k) |
| 89 |
); |
| 90 |
println!("{}", ui::kv("name", &k.name)); |
| 91 |
println!("{}", ui::kv("files", files.to_string())); |
| 92 |
} |
| 93 |
Ok(0) |
| 94 |
} |
| 95 |
|
| 96 |
pub fn list(as_json_out: bool) -> Result<i32> { |
| 97 |
let repo = open()?; |
| 98 |
let names = cylinder::list(&repo)?; |
| 99 |
let kept: Vec<cylinder::Kept> = names |
| 100 |
.iter() |
| 101 |
.map(|n| cylinder::open(&repo, n)) |
| 102 |
.collect::<std::result::Result<_, _>>()?; |
| 103 |
if as_json_out { |
| 104 |
let items: Vec<String> = kept |
| 105 |
.iter() |
| 106 |
.map(|k| { |
| 107 |
let files = cylinder::tree(&repo, &k.artifact) |
| 108 |
.map(|t| t.entries.len()) |
| 109 |
.unwrap_or(0); |
| 110 |
as_json(k, files) |
| 111 |
}) |
| 112 |
.collect(); |
| 113 |
println!("[{}]", items.join(",")); |
| 114 |
return Ok(0); |
| 115 |
} |
| 116 |
if kept.is_empty() { |
| 117 |
println!("No artifacts yet: oxy artifact pack DIR --kind archive"); |
| 118 |
} |
| 119 |
for k in &kept { |
| 120 |
println!("{}", summary(k)); |
| 121 |
} |
| 122 |
Ok(0) |
| 123 |
} |
| 124 |
|
| 125 |
pub fn show(name: &str, as_json_out: bool) -> Result<i32> { |
| 126 |
let repo = open()?; |
| 127 |
let k = cylinder::open(&repo, name)?; |
| 128 |
let t = cylinder::tree(&repo, &k.artifact)?; |
| 129 |
if as_json_out { |
| 130 |
println!("{}", as_json(&k, t.entries.len())); |
| 131 |
return Ok(0); |
| 132 |
} |
| 133 |
println!("{}", summary(&k)); |
| 134 |
println!("{}", ui::kv("name", &k.name)); |
| 135 |
if let Some(e) = &k.artifact.entrypoint { |
| 136 |
println!("{}", ui::kv("entrypoint", e.join(" "))); |
| 137 |
} |
| 138 |
for (key, v) in &k.artifact.labels { |
| 139 |
println!("{}", ui::kv("label", format!("{key}={v}"))); |
| 140 |
} |
| 141 |
for (key, v) in &k.artifact.env { |
| 142 |
println!("{}", ui::kv("env", format!("{key}={v}"))); |
| 143 |
} |
| 144 |
for e in &t.entries { |
| 145 |
let mode = match e.mode { |
| 146 |
cylinder::MODE_EXEC => "x", |
| 147 |
cylinder::MODE_LINK => "l", |
| 148 |
_ => " ", |
| 149 |
}; |
| 150 |
println!(" {mode} {:>10} {}", e.size, e.path); |
| 151 |
} |
| 152 |
Ok(0) |
| 153 |
} |
| 154 |
|
| 155 |
pub fn unpack(name: &str, dest: &Path) -> Result<i32> { |
| 156 |
let repo = open()?; |
| 157 |
let k = cylinder::open(&repo, name)?; |
| 158 |
let n = cylinder::unpack(&repo, &k, dest)?; |
| 159 |
println!( |
| 160 |
"{} {} files of {} into {}", |
| 161 |
ui::done("Unpacked"), |
| 162 |
n, |
| 163 |
ui::hash(&k.name[..12]), |
| 164 |
ui::path(dest.display()) |
| 165 |
); |
| 166 |
Ok(0) |
| 167 |
} |
| 168 |
|
| 169 |
pub fn verify(name: &str) -> Result<i32> { |
| 170 |
let repo = open()?; |
| 171 |
let k = cylinder::open(&repo, name)?; |
| 172 |
let n = cylinder::verify(&repo, &k)?; |
| 173 |
println!( |
| 174 |
"{} {}: its name, tree and {} files check out", |
| 175 |
ui::done("Verified"), |
| 176 |
ui::hash(&k.name[..12]), |
| 177 |
n |
| 178 |
); |
| 179 |
Ok(0) |
| 180 |
} |