Skip to content

Commit 1c6a069

Browse files
committed
Properly deserialize and serialize
1 parent 85a86d9 commit 1c6a069

File tree

2 files changed

+172
-50
lines changed

2 files changed

+172
-50
lines changed

crates/turborepo-lockfiles/src/bun/de.rs

+56-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::VecDeque;
33
use serde::Deserialize;
44

55
use super::{PackageEntry, PackageInfo};
6+
use crate::bun::RootInfo;
67
// Comment explaining entry schemas taken from bun.lock.zig
78
// first index is resolution for each type of package
89
// npm -> [
@@ -25,7 +26,7 @@ impl<'de> Deserialize<'de> for PackageEntry {
2526
D: serde::Deserializer<'de>,
2627
{
2728
use serde::de;
28-
#[derive(Deserialize)]
29+
#[derive(Deserialize, Debug)]
2930
#[serde(untagged)]
3031
enum Vals {
3132
Str(String),
@@ -46,20 +47,50 @@ impl<'de> Deserialize<'de> for PackageEntry {
4647
};
4748
// For workspace packages deps are second element, rest have them as third
4849
// element
49-
let info = vals
50-
.pop_front()
51-
.and_then(val_to_info)
52-
.or_else(|| vals.pop_front().and_then(val_to_info));
50+
51+
let mut registry = None;
52+
let mut info = None;
53+
54+
if key.ends_with("@root:") {
55+
let root = vals.pop_front().and_then(|val| {
56+
serde_json::from_value::<RootInfo>(match val {
57+
Vals::Info(info) => {
58+
serde_json::to_value(info.other).expect("failed to convert info to value")
59+
}
60+
_ => return None,
61+
})
62+
.ok()
63+
});
64+
return Ok(Self {
65+
ident: key,
66+
info,
67+
registry,
68+
checksum: None,
69+
root,
70+
});
71+
}
72+
73+
// The second value can be either registry (string) or info (object)
74+
vals.pop_front().map(|val| match val {
75+
Vals::Str(reg) => registry = Some(reg),
76+
Vals::Info(package_info) => info = Some(*package_info),
77+
});
78+
79+
// Info will be next if we haven't already found it
80+
if info.is_none() {
81+
info = vals.pop_front().and_then(val_to_info);
82+
}
83+
84+
// Checksum is last
5385
let checksum = vals.pop_front().and_then(|val| match val {
5486
Vals::Str(sha) => Some(sha),
5587
Vals::Info(_) => None,
5688
});
89+
5790
Ok(Self {
5891
ident: key,
5992
info,
60-
// The rest are only necessary for serializing a lockfile and aren't needed until adding
61-
// `prune` support
62-
registry: None,
93+
registry,
6394
checksum,
6495
root: None,
6596
})
@@ -114,7 +145,7 @@ mod test {
114145
PackageEntry,
115146
PackageEntry {
116147
ident: "[email protected]".into(),
117-
registry: None,
148+
registry: Some("".into()),
118149
info: Some(PackageInfo {
119150
dependencies: Some(("is-number".into(), "^6.0.0".into()))
120151
.into_iter()
@@ -142,10 +173,26 @@ mod test {
142173
root: None,
143174
}
144175
);
176+
177+
fixture!(
178+
root_pkg,
179+
PackageEntry,
180+
PackageEntry {
181+
ident: "some-package@root:".into(),
182+
root: Some(RootInfo {
183+
bin: Some("bin".into()),
184+
bin_dir: Some("binDir".into()),
185+
}),
186+
info: None,
187+
registry: None,
188+
checksum: None,
189+
}
190+
);
145191
#[test_case(json!({"name": "bun-test", "devDependencies": {"turbo": "^2.3.3"}}), basic_workspace() ; "basic")]
146192
#[test_case(json!({"name": "docs", "version": "0.1.0"}), workspace_with_version() ; "with version")]
147193
#[test_case(json!(["[email protected]", "", {"dependencies": {"is-number": "^6.0.0"}}, "sha"]), registry_pkg() ; "registry package")]
148194
#[test_case(json!(["docs", {"dependencies": {"is-odd": "3.0.1"}}]), workspace_pkg() ; "workspace package")]
195+
#[test_case(json!(["some-package@root:", {"bin": "bin", "binDir": "binDir"}]), root_pkg() ; "root package")]
149196
fn test_deserialization<T: for<'a> Deserialize<'a> + PartialEq + std::fmt::Debug>(
150197
input: serde_json::Value,
151198
expected: &T,

crates/turborepo-lockfiles/src/bun/ser.rs

+116-41
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,156 @@ use serde::{ser::SerializeTuple, Serialize};
22

33
use super::PackageEntry;
44

5+
// Comment explaining entry schemas taken from bun.lock.zig
6+
// first index is resolution for each type of package
7+
// npm -> [
8+
// "name@version",
9+
// registry (TODO: remove if default),
10+
// INFO,
11+
// integrity
12+
// ]
13+
// symlink -> [ "name@link:path", INFO ]
14+
// folder -> [ "name@file:path", INFO ]
15+
// workspace -> [ "name@workspace:path", INFO ]
16+
// tarball -> [ "name@tarball", INFO ]
17+
// root -> [ "name@root:", { bin, binDir } ]
18+
// git -> [ "name@git+repo", INFO, .bun-tag string (TODO: remove this) ]
19+
// github -> [ "name@github:user/repo", INFO, .bun-tag string (TODO: remove
20+
// this) ]
521
impl Serialize for PackageEntry {
622
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
7-
let mut tuple = serializer.serialize_tuple(5)?;
23+
let mut tuple = serializer.serialize_tuple(4)?;
824
tuple.serialize_element(&self.ident)?;
925

26+
// For root packages, only thing left to serialize is the root info
27+
if let Some(root) = &self.root {
28+
tuple.serialize_element(root)?;
29+
return tuple.end();
30+
}
31+
32+
// npm packages have a registry
33+
if let Some(registry) = &self.registry {
34+
tuple.serialize_element(registry)?;
35+
}
36+
37+
// All packages have info in the next slot
1038
if let Some(info) = &self.info {
11-
tuple.serialize_element(&self.registry.as_deref().unwrap_or(""))?;
1239
tuple.serialize_element(info)?;
13-
tuple.serialize_element(&self.checksum)?;
14-
if let Some(root) = &self.root {
15-
tuple.serialize_element(root)?;
16-
}
1740
};
1841

42+
// npm packages, git, and github have a checksum/integrity
43+
if let Some(checksum) = &self.checksum {
44+
tuple.serialize_element(checksum)?;
45+
}
46+
1947
tuple.end()
2048
}
2149
}
2250

2351
#[cfg(test)]
2452
mod test {
53+
use std::{str::FromStr, sync::OnceLock};
54+
2555
use serde_json::json;
56+
use test_case::test_case;
2657

2758
use super::*;
28-
use crate::bun::PackageInfo;
59+
use crate::{
60+
bun::{PackageInfo, RootInfo, WorkspaceEntry},
61+
BunLockfile,
62+
};
63+
64+
macro_rules! fixture {
65+
($name:ident, $kind:ty, $cons:expr) => {
66+
fn $name() -> &'static $kind {
67+
static ONCE: OnceLock<$kind> = OnceLock::new();
68+
ONCE.get_or_init(|| $cons)
69+
}
70+
};
71+
}
72+
73+
fixture!(
74+
basic_workspace,
75+
WorkspaceEntry,
76+
WorkspaceEntry {
77+
name: "bun-test".into(),
78+
dev_dependencies: Some(
79+
Some(("turbo".to_string(), "^2.3.3".to_string()))
80+
.into_iter()
81+
.collect()
82+
),
83+
..Default::default()
84+
}
85+
);
86+
87+
fixture!(
88+
workspace_with_version,
89+
WorkspaceEntry,
90+
WorkspaceEntry {
91+
name: "docs".into(),
92+
version: Some("0.1.0".into()),
93+
..Default::default()
94+
}
95+
);
2996

30-
#[test]
31-
fn test_serialize_registry_package() {
32-
let package = PackageEntry {
97+
fixture!(
98+
registry_pkg,
99+
PackageEntry,
100+
PackageEntry {
33101
ident: "[email protected]".into(),
34-
registry: Some("registry".into()),
102+
registry: Some("".into()),
35103
info: Some(PackageInfo {
36-
dependencies: [("is-number".into(), "^6.0.0".into())]
104+
dependencies: Some(("is-number".into(), "^6.0.0".into()))
37105
.into_iter()
38106
.collect(),
39107
..Default::default()
40108
}),
41109
checksum: Some("sha".into()),
42110
root: None,
43-
};
44-
45-
let expected =
46-
json!(["[email protected]", "registry", {"dependencies": {"is-number": "^6.0.0"}}, "sha"]);
47-
let actual = serde_json::to_value(&package).unwrap();
48-
assert_eq!(actual, expected);
49-
}
111+
}
112+
);
50113

51-
#[test]
52-
fn test_serialize_registry_package_no_deps() {
53-
let package = PackageEntry {
54-
ident: "[email protected]".into(),
55-
registry: Some("registry".into()),
114+
fixture!(
115+
workspace_pkg,
116+
PackageEntry,
117+
PackageEntry {
118+
ident: "docs".into(),
56119
info: Some(PackageInfo {
120+
dependencies: Some(("is-odd".into(), "3.0.1".into()))
121+
.into_iter()
122+
.collect(),
57123
..Default::default()
58124
}),
59-
checksum: Some("sha".into()),
60-
root: None,
61-
};
62-
63-
let expected = json!(["[email protected]", "registry", {}, "sha"]);
64-
let actual = serde_json::to_value(&package).unwrap();
65-
assert_eq!(actual, expected);
66-
}
67-
68-
#[test]
69-
fn test_serialize_workspace_package() {
70-
let package = PackageEntry {
71-
ident: "@workspace/package".into(),
72125
registry: None,
73-
info: None,
74126
checksum: None,
75127
root: None,
76-
};
128+
}
129+
);
77130

78-
let expected = json!(["@workspace/package"]);
79-
let actual = serde_json::to_value(&package).unwrap();
131+
fixture!(
132+
root_pkg,
133+
PackageEntry,
134+
PackageEntry {
135+
ident: "some-package@root:".into(),
136+
root: Some(RootInfo {
137+
bin: Some("bin".into()),
138+
bin_dir: Some("binDir".into()),
139+
}),
140+
info: None,
141+
registry: None,
142+
checksum: None,
143+
}
144+
);
145+
#[test_case(json!({"name": "bun-test", "devDependencies": {"turbo": "^2.3.3"}}), basic_workspace() ; "basic")]
146+
#[test_case(json!({"name": "docs", "version": "0.1.0"}), workspace_with_version() ; "with version")]
147+
#[test_case(json!(["[email protected]", "", {"dependencies": {"is-number": "^6.0.0"}}, "sha"]), registry_pkg() ; "registry package")]
148+
#[test_case(json!(["docs", {"dependencies": {"is-odd": "3.0.1"}}]), workspace_pkg() ; "workspace package")]
149+
#[test_case(json!(["some-package@root:", {"bin": "bin", "binDir": "binDir"}]), root_pkg() ; "root package")]
150+
fn test_serialization<T: for<'a> Serialize + PartialEq + std::fmt::Debug>(
151+
expected: serde_json::Value,
152+
input: &T,
153+
) {
154+
let actual = serde_json::to_value(input).unwrap();
80155
assert_eq!(actual, expected);
81156
}
82157
}

0 commit comments

Comments
 (0)