use serde::Deserialize;
use std::collections::BTreeMap;
#[derive(Debug, Deserialize)]
pub(super) struct RawBunLockfile {
#[serde(rename = "lockfileVersion")]
pub(super) lockfile_version: u32,
#[serde(default = "default_config_version", rename = "configVersion")]
pub(super) config_version: u32,
#[serde(default)]
pub(super) workspaces: BTreeMap<String, RawBunWorkspace>,
#[serde(default)]
pub(super) packages: BTreeMap<String, Vec<serde_json::Value>>,
#[serde(default)]
pub(super) overrides: BTreeMap<String, String>,
#[serde(default, rename = "patchedDependencies")]
pub(super) patched_dependencies: BTreeMap<String, String>,
#[serde(default, rename = "trustedDependencies")]
pub(super) trusted_dependencies: Vec<String>,
#[serde(default)]
pub(super) catalog: BTreeMap<String, String>,
#[serde(default)]
pub(super) catalogs: BTreeMap<String, BTreeMap<String, String>>,
#[serde(flatten)]
pub(super) extra: BTreeMap<String, serde_json::Value>,
}
fn default_config_version() -> u32 {
1
}
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct RawBunWorkspace {
#[serde(default)]
pub(super) dependencies: BTreeMap<String, String>,
#[serde(default)]
pub(super) dev_dependencies: BTreeMap<String, String>,
#[serde(default)]
pub(super) optional_dependencies: BTreeMap<String, String>,
#[serde(flatten)]
pub(super) extra: BTreeMap<String, serde_json::Value>,
}
#[derive(Debug, Default)]
pub(super) struct BunEntry {
pub(super) ident: String,
pub(super) meta: RawBunMeta,
pub(super) integrity: Option<String>,
}
impl BunEntry {
pub(super) fn from_array(key: &str, arr: &[serde_json::Value]) -> Result<Self, String> {
let ident = arr
.first()
.and_then(|v| v.as_str())
.ok_or_else(|| format!("package '{key}' has no ident string at position 0"))?
.to_string();
let mut meta = RawBunMeta::default();
let mut integrity: Option<String> = None;
for el in arr.iter().skip(1) {
match el {
serde_json::Value::Object(_) => {
meta = serde_json::from_value(el.clone()).unwrap_or_default();
}
serde_json::Value::String(s) if is_integrity_hash(s) => {
integrity = Some(s.clone());
}
_ => {}
}
}
Ok(Self {
ident,
meta,
integrity,
})
}
}
pub(super) fn is_integrity_hash(s: &str) -> bool {
let Some((algo, body)) = s.split_once('-') else {
return false;
};
let expected_len = match algo {
"sha512" => 88,
"sha384" => 64,
"sha256" => 44,
"sha1" => 28,
"md5" => 24,
_ => return false,
};
if body.len() != expected_len {
return false;
}
body.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'+' || b == b'/' || b == b'=')
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct RawBunMeta {
#[serde(default)]
pub(super) dependencies: BTreeMap<String, String>,
#[serde(default)]
pub(super) optional_dependencies: BTreeMap<String, String>,
#[serde(default)]
pub(super) peer_dependencies: BTreeMap<String, String>,
#[serde(default)]
pub(super) optional_peers: Vec<String>,
#[serde(default)]
pub(super) bin: serde_json::Value,
#[serde(default, deserialize_with = "aube_util::string_or_seq")]
pub(super) os: Vec<String>,
#[serde(default, deserialize_with = "aube_util::string_or_seq")]
pub(super) cpu: Vec<String>,
#[serde(default, deserialize_with = "aube_util::string_or_seq")]
pub(super) libc: Vec<String>,
#[serde(flatten)]
pub(super) extra: BTreeMap<String, serde_json::Value>,
}