Skip to content

Commit 8adb561

Browse files
committed
perf(core): avoid deep cloning json values
1 parent b1fd0e3 commit 8adb561

2 files changed

Lines changed: 44 additions & 23 deletions

File tree

src/package_json.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use {
44
serde::Serialize,
55
serde_json::{ser::PrettyFormatter, Serializer, Value},
66
std::{
7-
cell::RefCell,
7+
cell::{Ref, RefCell},
88
fs,
99
path::{Path, PathBuf},
1010
rc::Rc,
@@ -99,6 +99,26 @@ impl PackageJson {
9999
self.contents.borrow().pointer(pointer).cloned()
100100
}
101101

102+
/// Get a string property without cloning the entire Value tree.
103+
pub fn get_string_prop(&self, pointer: &str) -> Option<String> {
104+
self
105+
.contents
106+
.borrow()
107+
.pointer(pointer)
108+
.and_then(|v| v.as_str())
109+
.map(|s| s.to_string())
110+
}
111+
112+
/// Get a borrowed reference to an object property.
113+
/// Returns a Ref guard that keeps the borrow alive.
114+
/// Use this when you only need to iterate/read the object, not store it.
115+
pub fn get_object_prop_ref(&self, pointer: &str) -> Option<Ref<serde_json::Map<String, Value>>> {
116+
Ref::filter_map(self.contents.borrow(), |contents| {
117+
contents.pointer(pointer).and_then(|v| v.as_object())
118+
})
119+
.ok()
120+
}
121+
102122
/// Deeply set a property in the parsed package.json
103123
pub fn set_prop(&self, pointer: &str, next_value: Value) {
104124
if pointer == "/" {

src/packages.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ impl Packages {
8787
.iter()
8888
.filter_map(|package| -> Option<(String, Rc<Specifier>)> {
8989
let package = package.borrow();
90-
let name = package.get_prop("/name");
91-
let version = package.get_prop("/version");
92-
if let (Some(Value::String(name)), Some(Value::String(version))) = (name, version) {
90+
let name = package.get_string_prop("/name");
91+
let version = package.get_string_prop("/version");
92+
if let (Some(name), Some(version)) = (name, version) {
9393
Some((name, Specifier::new(&version)))
9494
} else {
9595
None
@@ -108,30 +108,31 @@ impl Packages {
108108
for dependency_type in all_dependency_types {
109109
match dependency_type.strategy {
110110
Strategy::NameAndVersionProps => {
111-
if let (Some(Value::String(name)), Some(Value::String(raw_specifier))) = (
112-
package.borrow().get_prop(dependency_type.name_path.as_ref().unwrap()),
113-
package.borrow().get_prop(&dependency_type.path).or_else(|| {
114-
// Ensure that instances are still created for local packages
115-
// which are missing a version
116-
if dependency_type.name == "local" {
117-
Some(Value::String("".to_string()))
118-
} else {
119-
None
120-
}
121-
}),
122-
) {
111+
let package_borrowed = package.borrow();
112+
let name = package_borrowed.get_string_prop(dependency_type.name_path.as_ref().unwrap());
113+
let raw_specifier = package_borrowed.get_string_prop(&dependency_type.path).or_else(|| {
114+
// Ensure that instances are still created for local packages
115+
// which are missing a version
116+
if dependency_type.name == "local" {
117+
Some("".to_string())
118+
} else {
119+
None
120+
}
121+
});
122+
123+
if let (Some(name), Some(raw_specifier)) = (name, raw_specifier) {
123124
on_instance(InstanceDescriptor {
124125
dependency_type: dependency_type.clone(),
125-
internal_name: name.to_string(),
126+
internal_name: name.clone(),
126127
matches_cli_filter: false,
127-
name: name.to_string(),
128+
name,
128129
package: Rc::clone(package),
129130
specifier: Specifier::new(&raw_specifier),
130131
});
131132
}
132133
}
133134
Strategy::NamedVersionString => {
134-
if let Some(Value::String(specifier)) = package.borrow().get_prop(&dependency_type.path) {
135+
if let Some(specifier) = package.borrow().get_string_prop(&dependency_type.path) {
135136
if let Some((name, raw_specifier)) = specifier.split_once('@') {
136137
on_instance(InstanceDescriptor {
137138
dependency_type: dependency_type.clone(),
@@ -145,7 +146,7 @@ impl Packages {
145146
}
146147
}
147148
Strategy::UnnamedVersionString => {
148-
if let Some(Value::String(raw_specifier)) = package.borrow().get_prop(&dependency_type.path) {
149+
if let Some(raw_specifier) = package.borrow().get_string_prop(&dependency_type.path) {
149150
on_instance(InstanceDescriptor {
150151
dependency_type: dependency_type.clone(),
151152
internal_name: dependency_type.name.clone(),
@@ -157,16 +158,16 @@ impl Packages {
157158
}
158159
}
159160
Strategy::VersionsByName => {
160-
if let Some(Value::Object(versions_by_name)) = package.borrow().get_prop(&dependency_type.path) {
161-
for (name, raw_specifier) in versions_by_name {
161+
if let Some(versions_by_name_ref) = package.borrow().get_object_prop_ref(&dependency_type.path) {
162+
for (name, raw_specifier) in versions_by_name_ref.iter() {
162163
if let Value::String(raw_specifier) = raw_specifier {
163164
on_instance(InstanceDescriptor {
164165
dependency_type: dependency_type.clone(),
165166
internal_name: name.to_string(),
166167
matches_cli_filter: false,
167168
name: name.to_string(),
168169
package: Rc::clone(package),
169-
specifier: Specifier::new(&raw_specifier),
170+
specifier: Specifier::new(raw_specifier),
170171
});
171172
}
172173
}

0 commit comments

Comments
 (0)