Skip to content

Commit dcc6b4e

Browse files
authored
zip downloading (#36)
allows installing from git
1 parent b721fe1 commit dcc6b4e

File tree

10 files changed

+512
-178
lines changed

10 files changed

+512
-178
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "godot-package-manager"
3-
version = "1.3.2"
3+
version = "1.4.0"
44
edition = "2021"
55
authors = ["bendn <bend.n@outlook.com>"]
66
description = "A package manager for godot"
@@ -12,13 +12,14 @@ license = "Apache-2.0"
1212
[dependencies]
1313
clap = { version = "4.0.29", features = ["derive"] }
1414
deser-hjson = "1.0.2"
15-
flate2 = "1.0.25"
1615
lazy_static = "1.4.0"
1716
regex = "1.7.0"
1817
serde = { version = "1.0.150", features = ["derive"] }
1918
serde_json = "1.0.89"
2019
serde_yaml = "0.9.14"
2120
tar = "0.4.38"
21+
flate2 = "1.0.25"
22+
zip = { version = "0.6", features = ["bzip2"] }
2223
toml = "0.5.10"
2324
sha1 = "0.10.5"
2425
console = "0.15.4"

godot.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
},
77
{
88
"name": "@bendn/splitter",
9-
"tarball": "https://registry.npmjs.org/@bendn/splitter/-/splitter-1.0.6.tgz",
10-
"version": "1.0.6"
9+
"tarball": "https://github.com/bend-n/splitter/archive/refs/heads/main.zip",
10+
"version": "1.1.0"
1111
},
1212
{
1313
"name": "@bendn/stockfish.gd",

godot.package

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
packages: {
22
@bendn/test: "^2.0.0"
3-
@bendn/splitter: "1.0.x"
43
@bendn/stockfish.gd: "1.*"
5-
}
4+
"https://github.com/bend-n/splitter/archive/refs/heads/main.zip": "1.0.x"
5+
}

src/archive.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
use crate::config_file::*;
2+
use crate::ctx;
3+
use crate::package::Package;
4+
use crate::Client;
5+
use anyhow::{Context, Result};
6+
use flate2::bufread::GzDecoder;
7+
use serde::Serialize;
8+
use std::fmt::Display;
9+
use std::fs::{create_dir_all, set_permissions, File, Permissions};
10+
use std::io::{self, prelude::*, Cursor};
11+
use std::path::{Component::Normal, Path, PathBuf};
12+
use tar::Archive as Tarchive;
13+
use tar::EntryType::Directory;
14+
use zip::result::{ZipError, ZipResult};
15+
use zip::ZipArchive as Zarchive;
16+
17+
type TArch = Tarchive<GzDecoder<Cursor<Vec<u8>>>>;
18+
type ZArch = Zarchive<Cursor<Vec<u8>>>;
19+
20+
#[derive(Default, Clone, Serialize, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)]
21+
pub struct Data {
22+
#[serde(skip)]
23+
pub bytes: Vec<u8>,
24+
pub uri: String,
25+
}
26+
27+
impl Data {
28+
pub fn new(bytes: Vec<u8>, uri: String) -> Self {
29+
Self { bytes, uri }
30+
}
31+
pub fn new_bytes(bytes: Vec<u8>) -> Self {
32+
Self {
33+
bytes,
34+
uri: String::new(),
35+
}
36+
}
37+
pub fn new_uri(uri: String) -> Self {
38+
Self { bytes: vec![], uri }
39+
}
40+
}
41+
42+
#[derive(Default, Clone, Serialize, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)]
43+
#[serde(untagged)]
44+
pub enum CompressionType {
45+
Gzip(Data),
46+
Zip(Data),
47+
Lock(String),
48+
#[default]
49+
None,
50+
}
51+
52+
impl Display for CompressionType {
53+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54+
match self {
55+
CompressionType::Gzip(d) => write!(f, "{}", d.uri),
56+
CompressionType::Zip(d) => write!(f, "{}", d.uri),
57+
CompressionType::Lock(d) => write!(f, "{}", d),
58+
_ => unreachable!(),
59+
}
60+
}
61+
}
62+
63+
impl CompressionType {
64+
pub fn from(ty: &str, bytes: Vec<u8>, uri: String) -> Self {
65+
match ty {
66+
"zip" => Self::Zip(Data::new(bytes, uri)),
67+
_ => Self::Gzip(Data::new(bytes, uri)),
68+
}
69+
}
70+
71+
pub fn lock(&mut self) {
72+
*self = Self::Lock(match self {
73+
CompressionType::Gzip(d) => std::mem::take(&mut d.uri),
74+
CompressionType::Zip(d) => std::mem::take(&mut d.uri),
75+
_ => unreachable!(),
76+
})
77+
}
78+
}
79+
80+
enum ArchiveType {
81+
Gzip(Box<TArch>),
82+
Zip(ZArch),
83+
}
84+
85+
pub struct Archive {
86+
inner: ArchiveType,
87+
uri: String,
88+
}
89+
90+
// impl<'a, Z> From<TArch<'a>> for Archive<'a> {
91+
// fn from(value: TArch<'a>) -> Self {
92+
// Self::Gzip(value)
93+
// }
94+
// }
95+
96+
// impl<'a> From<ZArch<'a>> for Archive<'a> {
97+
// fn from(value: ZArch<'a>) -> Self {
98+
// Self::Zip(value)
99+
// }
100+
// }
101+
102+
fn unpack_zarchive(archive: &mut ZArch, dst: &Path) -> ZipResult<()> {
103+
if dst.symlink_metadata().is_err() {
104+
create_dir_all(dst).map_err(ZipError::Io)?;
105+
}
106+
let dst = &dst.canonicalize().unwrap_or(dst.to_path_buf());
107+
108+
let mut directories = vec![];
109+
for i in 0..archive.len() {
110+
let mut file = archive.by_index(i)?;
111+
let path = dst.join(skip_toplevel(
112+
file.enclosed_name().ok_or(ZipError::FileNotFound)?,
113+
));
114+
if file.is_dir() {
115+
directories.push(path);
116+
} else {
117+
create_dir_all(path.parent().unwrap())?;
118+
let mut outfile = File::create(&path)?;
119+
io::copy(&mut file, &mut outfile)?;
120+
#[cfg(unix)]
121+
{
122+
use std::os::unix::fs::PermissionsExt;
123+
if let Some(mode) = file.unix_mode() {
124+
set_permissions(&path, Permissions::from_mode(mode))?;
125+
}
126+
}
127+
}
128+
}
129+
for path in directories {
130+
create_dir_all(path)?;
131+
}
132+
Ok(())
133+
}
134+
135+
fn skip_toplevel(p: &Path) -> PathBuf {
136+
p.components()
137+
.skip(1)
138+
.filter(|c| matches!(c, Normal(_)))
139+
.collect::<PathBuf>()
140+
}
141+
142+
fn unpack_tarchive(archive: &mut TArch, dst: &Path) -> io::Result<()> {
143+
if dst.symlink_metadata().is_err() {
144+
create_dir_all(dst)?;
145+
}
146+
147+
let dst = &dst.canonicalize().unwrap_or(dst.to_path_buf());
148+
149+
// Delay any directory entries until the end (they will be created if needed by
150+
// descendants), to ensure that directory permissions do not interfer with descendant
151+
// extraction.
152+
let mut directories = Vec::new();
153+
for entry in archive.entries()? {
154+
let entry = entry?;
155+
let mut entry = (dst.join(skip_toplevel(&entry.path()?)), entry);
156+
if entry.1.header().entry_type() == Directory {
157+
directories.push(entry);
158+
} else {
159+
create_dir_all(entry.0.parent().unwrap())?;
160+
entry.1.unpack(entry.0)?;
161+
}
162+
}
163+
for mut dir in directories {
164+
dir.1.unpack(dir.0)?;
165+
}
166+
Ok(())
167+
}
168+
169+
fn get_zfile(zarchive: &mut ZArch, search: &str, out: &mut String) -> ZipResult<()> {
170+
for i in 0..zarchive.len() {
171+
let mut file = zarchive.by_index(i)?;
172+
if let Some(n) = file.enclosed_name() {
173+
if let Some(base) = &Path::new(n).file_name() {
174+
if base.to_string_lossy() == search {
175+
file.read_to_string(out)?;
176+
return Ok(());
177+
}
178+
}
179+
}
180+
}
181+
Err(ZipError::FileNotFound)
182+
}
183+
184+
fn get_gfile(tarchive: &mut TArch, file: &str, out: &mut String) -> io::Result<()> {
185+
for entry in tarchive.entries()? {
186+
let mut entry = entry?;
187+
if let Ok(p) = entry.path() {
188+
if p.file_name().ok_or(io::ErrorKind::InvalidData)? == file {
189+
entry.read_to_string(out)?;
190+
return Ok(());
191+
}
192+
}
193+
}
194+
Err(io::ErrorKind::InvalidData.into())
195+
}
196+
197+
impl Archive {
198+
pub fn unpack(&mut self, dst: &Path) -> Result<()> {
199+
match &mut self.inner {
200+
ArchiveType::Gzip(g) => unpack_tarchive(g, dst)?,
201+
ArchiveType::Zip(z) => unpack_zarchive(z, dst)?,
202+
}
203+
Ok(())
204+
}
205+
206+
pub fn get_file(&mut self, file: &str, out: &mut String) -> Result<()> {
207+
match &mut self.inner {
208+
ArchiveType::Gzip(g) => get_gfile(g, file, out)?,
209+
ArchiveType::Zip(z) => get_zfile(z, file, out)?,
210+
}
211+
Ok(())
212+
}
213+
214+
fn wrap(wrap: ArchiveType, uri: String) -> Self {
215+
Self { inner: wrap, uri }
216+
}
217+
218+
pub fn new(value: CompressionType) -> Result<Self> {
219+
match value {
220+
CompressionType::Gzip(data) => Ok(Self::new_gzip(data.bytes, data.uri)),
221+
CompressionType::Zip(data) => Self::new_zip(data.bytes, data.uri),
222+
_ => unreachable!(),
223+
}
224+
}
225+
226+
pub fn new_gzip(value: Vec<u8>, uri: String) -> Self {
227+
Self::wrap(
228+
ArchiveType::Gzip(Box::new(Tarchive::new(GzDecoder::new(Cursor::new(value))))),
229+
uri,
230+
)
231+
}
232+
233+
pub fn new_zip(value: Vec<u8>, uri: String) -> Result<Self> {
234+
Ok(Self::wrap(
235+
ArchiveType::Zip(Zarchive::new(Cursor::new(value))?),
236+
uri,
237+
))
238+
}
239+
/// async trait + lifetimes = boom
240+
pub async fn into_package(mut self, client: Client) -> Result<Package> {
241+
let mut contents = String::new();
242+
{
243+
ctx!(
244+
self.get_file("package.json", &mut contents),
245+
"searching for package.json"
246+
)?;
247+
}
248+
let ty = match self.inner {
249+
ArchiveType::Zip(_) => CompressionType::Zip(Data::new_uri(self.uri)),
250+
ArchiveType::Gzip(_) => CompressionType::Gzip(Data::new_uri(self.uri)),
251+
};
252+
ctx!(
253+
ctx!(
254+
ConfigFile::parse(&contents, ConfigType::JSON, client).await,
255+
"parsing config file from package.json inside zipfile"
256+
)?
257+
.into_package(ty),
258+
"turning config file into package"
259+
)
260+
}
261+
}

src/cache.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::archive::*;
2+
use crate::conversions::TryIntoAsync;
13
use crate::package::parsing::{Packument, ParsedManifest, ParsedPackage};
24
use crate::package::Package;
35
use crate::{ctx, Client};
@@ -127,15 +129,22 @@ impl std::fmt::Debug for VersionsCache {
127129
}
128130
}
129131

130-
#[derive(Debug, Clone, Default)] // yuck, a clone
132+
#[derive(Default, Clone)] // yuck, a clone
131133
pub enum CacheEntry {
132134
Unparsed(ParsedPackage),
133135
Parsed(Package),
134136
Manifest(ParsedManifest),
137+
Tarball(CompressionType),
135138
#[default]
136139
Empty,
137140
}
138141

142+
impl From<CompressionType> for CacheEntry {
143+
fn from(value: CompressionType) -> Self {
144+
Self::Tarball(value)
145+
}
146+
}
147+
139148
impl From<Package> for CacheEntry {
140149
fn from(value: Package) -> Self {
141150
Self::Parsed(value)
@@ -153,22 +162,23 @@ impl From<ParsedPackage> for CacheEntry {
153162
}
154163

155164
impl CacheEntry {
156-
pub async fn parse(&mut self, client: Client, cache: Cache, name: String) -> Result<()> {
157-
match self {
158-
CacheEntry::Unparsed(p) => {
159-
let p = std::mem::take(p).into_package(client, cache).await?;
160-
*self = CacheEntry::Parsed(p);
161-
}
165+
pub async fn parse(&mut self, client: Client, name: String) -> Result<()> {
166+
*self = CacheEntry::from(match self {
167+
CacheEntry::Unparsed(p) => std::mem::take(p).into_package(client).await?,
162168
CacheEntry::Manifest(m) => {
163169
let m = ctx!(
164-
std::mem::take(m).into_manifest(client, cache).await,
170+
std::mem::take(m).try_into_async(client).await,
165171
"parsing ParsedManifest into Manifest in get_package()"
166172
)?;
167-
let p = Package::from_manifest(m, name.clone());
168-
*self = CacheEntry::Parsed(p);
173+
Package::from_manifest(m, name.clone())
169174
}
170-
_ => {}
171-
}
175+
CacheEntry::Tarball(t) => {
176+
Archive::new(std::mem::take(t))?
177+
.into_package(client)
178+
.await?
179+
}
180+
_ => return Ok(()),
181+
});
172182
Ok(())
173183
}
174184

@@ -178,4 +188,11 @@ impl CacheEntry {
178188
_ => unreachable!(),
179189
}
180190
}
191+
192+
// pub fn get_bytes(&self) -> &Vec<u8> {
193+
// match self {
194+
// CacheEntry::Tarball(t) => t,
195+
// _ => unreachable!(),
196+
// }
197+
// }
181198
}

0 commit comments

Comments
 (0)