Skip to content

Commit 8ceafa6

Browse files
authored
make it fast (#25)
1 parent 1d5af9d commit 8ceafa6

File tree

6 files changed

+535
-283
lines changed

6 files changed

+535
-283
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ Cargo.lock
1515

1616
# Addons folder
1717
addons/
18+
19+
# Performance data
20+
perf.data*

Cargo.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "godot-package-manager"
3-
version = "1.1.2"
3+
version = "1.2.0"
44
edition = "2021"
55
authors = ["bendn <[email protected]>"]
66
description = "A package manager for godot"
@@ -15,7 +15,6 @@ deser-hjson = "1.0.2"
1515
flate2 = "1.0.25"
1616
lazy_static = "1.4.0"
1717
regex = "1.7.0"
18-
ureq = "2.5.0"
1918
serde = { version = "1.0.150", features = ["derive"] }
2019
serde_json = "1.0.89"
2120
serde_yaml = "0.9.14"
@@ -26,9 +25,16 @@ console = "0.15.4"
2625
indicatif = "0.17.2"
2726
anyhow = "1.0.68"
2827
dialoguer = { version = "0.10.3", features = [] }
29-
rayon = "1.6"
28+
reqwest = { version = "0.11", features = [] }
29+
tokio = { version = "1", features = ["full"] }
30+
async-recursion = "1.0.2"
31+
futures = "0.3"
3032

3133
[dev-dependencies]
3234
glob = "0.3.0"
3335
sha2 = "0.10.6"
3436
tempdir = "0.3.7"
37+
38+
[profile.release]
39+
lto = true
40+
strip = true

src/config_file.rs

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::package::Package;
22
use anyhow::Result;
33
use console::style;
4+
use futures::stream::{self, StreamExt};
45
use serde::{Deserialize, Serialize};
56
use std::collections::HashMap;
67

@@ -17,7 +18,7 @@ pub struct ConfigFile {
1718
/// A wrapper to [ConfigFile]. This _is_ necessary.
1819
/// Any alternatives will end up being more ugly than this. (trust me i tried)
1920
/// There is no way to automatically deserialize the map into a vec.
20-
struct ConfigWrapper {
21+
struct ParsedConfig {
2122
// support NPM package.json files (also allows gpm -c package.json -u)
2223
#[serde(alias = "dependencies")]
2324
packages: HashMap<String, String>,
@@ -36,33 +37,40 @@ impl std::fmt::Display for ConfigType {
3637
}
3738
}
3839

39-
impl From<ConfigWrapper> for ConfigFile {
40-
fn from(from: ConfigWrapper) -> Self {
40+
impl From<ConfigFile> for ParsedConfig {
41+
fn from(from: ConfigFile) -> Self {
4142
Self {
4243
packages: from
4344
.packages
4445
.into_iter()
45-
.map(|(name, version)| Package::new(name, version).unwrap())
46+
.map(|p| (p.name, p.version))
4647
.collect(),
4748
}
4849
}
4950
}
5051

51-
impl From<ConfigFile> for ConfigWrapper {
52-
fn from(from: ConfigFile) -> Self {
53-
Self {
54-
packages: from
55-
.packages
56-
.into_iter()
57-
.map(|p| (p.name, p.version))
58-
.collect(),
59-
}
52+
impl ParsedConfig {
53+
pub fn parse(txt: &str, t: ConfigType) -> Result<Self> {
54+
Ok(match t {
55+
ConfigType::TOML => toml::from_str::<ParsedConfig>(txt)?,
56+
ConfigType::JSON => deser_hjson::from_str::<ParsedConfig>(txt)?,
57+
ConfigType::YAML => serde_yaml::from_str::<ParsedConfig>(txt)?,
58+
})
59+
}
60+
61+
pub async fn into_configfile(self) -> ConfigFile {
62+
let packages = stream::iter(self.packages.into_iter())
63+
.map(|(name, version)| async { Package::new(name, version).await.unwrap() })
64+
.buffer_unordered(crate::PARALLEL)
65+
.collect::<Vec<Package>>()
66+
.await;
67+
ConfigFile { packages }
6068
}
6169
}
6270

6371
impl ConfigFile {
6472
pub fn print(self, t: ConfigType) -> String {
65-
let w = ConfigWrapper::from(self);
73+
let w = ParsedConfig::from(self);
6674
match t {
6775
ConfigType::JSON => serde_json::to_string_pretty(&w).unwrap(),
6876
ConfigType::YAML => serde_yaml::to_string(&w).unwrap(),
@@ -72,20 +80,24 @@ impl ConfigFile {
7280

7381
/// Creates a new [ConfigFile] from the given text
7482
/// Panics if the file cant be parsed as toml, hjson or yaml.
75-
pub fn new(contents: &String) -> Self {
76-
if contents.len() == 0 {
83+
pub async fn new(contents: &String) -> Self {
84+
if contents.is_empty() {
7785
panic!("Empty CFG");
7886
}
7987

8088
// definetly not going to backfire
8189
let mut cfg = if contents.as_bytes()[0] == b'{' {
8290
// json gets brute forced first so this isnt really needed
83-
Self::parse(contents, ConfigType::JSON).expect("Parsing CFG from JSON should work")
91+
Self::parse(contents, ConfigType::JSON)
92+
.await
93+
.expect("Parsing CFG from JSON should work")
8494
} else if contents.len() > 3 && contents[..3] == *"---" {
85-
Self::parse(contents, ConfigType::YAML).expect("Parsing CFG from YAML should work")
95+
Self::parse(contents, ConfigType::YAML)
96+
.await
97+
.expect("Parsing CFG from YAML should work")
8698
} else {
8799
for i in [ConfigType::JSON, ConfigType::YAML, ConfigType::TOML].into_iter() {
88-
let res = Self::parse(contents, i.clone());
100+
let res = Self::parse(contents, i).await;
89101

90102
// im sure theres some kind of idiomatic rust way to do this that i dont know of
91103
if res.is_ok() {
@@ -106,30 +118,19 @@ impl ConfigFile {
106118
cfg
107119
}
108120

109-
pub fn parse(txt: &String, t: ConfigType) -> Result<Self> {
110-
type W = ConfigWrapper;
111-
Ok(match t {
112-
ConfigType::TOML => toml::from_str::<W>(txt)?.into(),
113-
ConfigType::JSON => deser_hjson::from_str::<W>(txt)?.into(),
114-
ConfigType::YAML => serde_yaml::from_str::<W>(txt)?.into(),
115-
})
121+
pub async fn parse(txt: &str, t: ConfigType) -> Result<Self> {
122+
Ok(ParsedConfig::parse(txt, t)?.into_configfile().await)
116123
}
117124

118125
/// Creates a lockfile for this config file.
119126
/// note: Lockfiles are currently unused.
120-
pub fn lock(&mut self) -> String {
121-
let mut pkgs = vec![] as Vec<Package>;
122-
self.collect()
123-
.into_iter()
124-
.filter(|p| p.is_installed())
125-
.for_each(|mut p| {
126-
if p.integrity.is_empty() {
127-
p.integrity = p
128-
.get_integrity()
129-
.expect("Should be able to get package integrity");
130-
}
131-
pkgs.push(p);
132-
});
127+
pub async fn lock(&mut self) -> String {
128+
let mut pkgs = vec![];
129+
for mut p in self.collect() {
130+
if p.is_installed() && p.get_manifest().await.is_ok() {
131+
pkgs.push(p)
132+
};
133+
}
133134
serde_json::to_string_pretty(&pkgs).unwrap()
134135
}
135136

@@ -164,13 +165,13 @@ impl ConfigFile {
164165
mod tests {
165166
use crate::config_file::*;
166167

167-
#[test]
168-
fn parse() {
168+
#[tokio::test]
169+
async fn parse() {
169170
let _t = crate::test_utils::mktemp();
170171
let cfgs: [&mut ConfigFile; 3] = [
171-
&mut ConfigFile::new(&r#"dependencies: { "@bendn/test": 2.0.10 }"#.into()), // quoteless fails as a result of https://github.com/Canop/deser-hjson/issues/9
172-
&mut ConfigFile::new(&"dependencies:\n \"@bendn/test\": 2.0.10".into()),
173-
&mut ConfigFile::new(&"[dependencies]\n\"@bendn/test\" = \"2.0.10\"".into()),
172+
&mut ConfigFile::new(&r#"dependencies: { "@bendn/test": 2.0.10 }"#.into()).await, // quoteless fails as a result of https://github.com/Canop/deser-hjson/issues/9
173+
&mut ConfigFile::new(&"dependencies:\n \"@bendn/test\": 2.0.10".into()).await,
174+
&mut ConfigFile::new(&"[dependencies]\n\"@bendn/test\" = \"2.0.10\"".into()).await,
174175
];
175176
#[derive(Debug, Deserialize, Clone, Eq, PartialEq)]
176177
struct LockFileEntry {
@@ -189,9 +190,11 @@ mod tests {
189190
cfg.packages[0].dependencies[0].to_string(),
190191
191192
);
192-
cfg.for_each(|p| p.download());
193+
for mut p in cfg.collect() {
194+
p.download().await
195+
}
193196
assert_eq!(
194-
serde_json::from_str::<Vec<LockFileEntry>>(cfg.lock().as_str()).unwrap(),
197+
serde_json::from_str::<Vec<LockFileEntry>>(cfg.lock().await.as_str()).unwrap(),
195198
wanted_lockfile
196199
);
197200
}

0 commit comments

Comments
 (0)