mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 13:20:42 +00:00
Refactor: Replace direct signature fields with metadata-based storage
- Migrated `_SIGNATURE` handling in `CatalogPart`, `UpdateLog`, and `PackageUpdateEntry` to use metadata fields. - Introduced `set_signature` and `signature` methods for cleaner access and manipulation of signature data. - Updated deserialization logic to accommodate flattened metadata storage. - Improved structure by centralizing signature and metadata interactions.
This commit is contained in:
parent
7a3373a17d
commit
f8068364bc
2 changed files with 155 additions and 17 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
@ -205,23 +206,68 @@ pub struct PackageVersionEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Catalog part (base, dependency, summary)
|
/// Catalog part (base, dependency, summary)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct CatalogPart {
|
pub struct CatalogPart {
|
||||||
/// Packages by publisher and stem
|
/// Packages by publisher and stem
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub packages: BTreeMap<String, BTreeMap<String, Vec<PackageVersionEntry>>>,
|
pub packages: BTreeMap<String, BTreeMap<String, Vec<PackageVersionEntry>>>,
|
||||||
|
|
||||||
/// Optional signature information
|
/// Metadata fields (keys starting with '_')
|
||||||
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
#[serde(flatten)]
|
||||||
pub signature: Option<BTreeMap<String, String>>,
|
pub metadata: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for CatalogPart {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let all_entries = BTreeMap::<String, Value>::deserialize(deserializer)?;
|
||||||
|
let mut packages = BTreeMap::new();
|
||||||
|
let mut metadata = BTreeMap::new();
|
||||||
|
|
||||||
|
for (k, v) in all_entries {
|
||||||
|
if k.starts_with('_') {
|
||||||
|
metadata.insert(k, v);
|
||||||
|
} else {
|
||||||
|
// Try to parse as package map
|
||||||
|
if let Ok(pkg_map) = serde_json::from_value(v.clone()) {
|
||||||
|
packages.insert(k, pkg_map);
|
||||||
|
} else {
|
||||||
|
// If it fails, treat as metadata
|
||||||
|
metadata.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CatalogPart { packages, metadata })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CatalogPart {
|
impl CatalogPart {
|
||||||
/// Create a new catalog part
|
/// Create a new catalog part
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
CatalogPart {
|
CatalogPart {
|
||||||
signature: None,
|
|
||||||
packages: BTreeMap::new(),
|
packages: BTreeMap::new(),
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get signature information if present
|
||||||
|
pub fn signature(&self) -> Option<BTreeMap<String, String>> {
|
||||||
|
self.metadata.get("_SIGNATURE").and_then(|v| {
|
||||||
|
serde_json::from_value::<BTreeMap<String, String>>(v.clone()).ok()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set signature information
|
||||||
|
pub fn set_signature(&mut self, signature: Option<BTreeMap<String, String>>) {
|
||||||
|
if let Some(sig) = signature {
|
||||||
|
if let Ok(val) = serde_json::to_value(sig) {
|
||||||
|
self.metadata.insert("_SIGNATURE".to_string(), val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.metadata.remove("_SIGNATURE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,7 +342,7 @@ pub enum CatalogOperationType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Package update entry in an update log
|
/// Package update entry in an update log
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct PackageUpdateEntry {
|
pub struct PackageUpdateEntry {
|
||||||
/// Type of operation (add or remove)
|
/// Type of operation (add or remove)
|
||||||
#[serde(rename = "op-type")]
|
#[serde(rename = "op-type")]
|
||||||
|
|
@ -316,25 +362,116 @@ pub struct PackageUpdateEntry {
|
||||||
/// Optional SHA-1 signature of the package manifest
|
/// Optional SHA-1 signature of the package manifest
|
||||||
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature_sha1: Option<String>,
|
pub signature_sha1: Option<String>,
|
||||||
|
|
||||||
|
/// Metadata fields (keys starting with '_')
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub metadata: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PackageUpdateEntry {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Internal {
|
||||||
|
#[serde(rename = "op-type")]
|
||||||
|
op_type: CatalogOperationType,
|
||||||
|
#[serde(rename = "op-time")]
|
||||||
|
op_time: String,
|
||||||
|
version: String,
|
||||||
|
#[serde(rename = "signature-sha-1")]
|
||||||
|
signature_sha1: Option<String>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
all_entries: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let internal = Internal::deserialize(deserializer)?;
|
||||||
|
let mut catalog_parts = BTreeMap::new();
|
||||||
|
let mut metadata = BTreeMap::new();
|
||||||
|
|
||||||
|
for (k, v) in internal.all_entries {
|
||||||
|
if k.starts_with('_') {
|
||||||
|
metadata.insert(k, v);
|
||||||
|
} else {
|
||||||
|
if let Ok(part_map) = serde_json::from_value(v.clone()) {
|
||||||
|
catalog_parts.insert(k, part_map);
|
||||||
|
} else {
|
||||||
|
metadata.insert(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PackageUpdateEntry {
|
||||||
|
op_type: internal.op_type,
|
||||||
|
op_time: internal.op_time,
|
||||||
|
version: internal.version,
|
||||||
|
catalog_parts,
|
||||||
|
signature_sha1: internal.signature_sha1,
|
||||||
|
metadata,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update log
|
/// Update log
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct UpdateLog {
|
pub struct UpdateLog {
|
||||||
/// Updates by publisher and stem
|
/// Updates by publisher and stem
|
||||||
pub updates: BTreeMap<String, BTreeMap<String, Vec<PackageUpdateEntry>>>,
|
pub updates: BTreeMap<String, BTreeMap<String, Vec<PackageUpdateEntry>>>,
|
||||||
|
|
||||||
/// Optional signature information
|
/// Metadata fields (keys starting with '_')
|
||||||
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
#[serde(flatten)]
|
||||||
pub signature: Option<BTreeMap<String, String>>,
|
pub metadata: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for UpdateLog {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Internal {
|
||||||
|
#[serde(default)]
|
||||||
|
updates: BTreeMap<String, BTreeMap<String, Vec<PackageUpdateEntry>>>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
all_entries: BTreeMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let internal = Internal::deserialize(deserializer)?;
|
||||||
|
let mut metadata = internal.all_entries;
|
||||||
|
metadata.remove("updates");
|
||||||
|
|
||||||
|
Ok(UpdateLog {
|
||||||
|
updates: internal.updates,
|
||||||
|
metadata,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateLog {
|
impl UpdateLog {
|
||||||
/// Create a new update log
|
/// Create a new update log
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
UpdateLog {
|
UpdateLog {
|
||||||
signature: None,
|
|
||||||
updates: BTreeMap::new(),
|
updates: BTreeMap::new(),
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get signature information if present
|
||||||
|
pub fn signature(&self) -> Option<BTreeMap<String, String>> {
|
||||||
|
self.metadata.get("_SIGNATURE").and_then(|v| {
|
||||||
|
serde_json::from_value::<BTreeMap<String, String>>(v.clone()).ok()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set signature information
|
||||||
|
pub fn set_signature(&mut self, signature: Option<BTreeMap<String, String>>) {
|
||||||
|
if let Some(sig) = signature {
|
||||||
|
if let Ok(val) = serde_json::to_value(sig) {
|
||||||
|
self.metadata.insert("_SIGNATURE".to_string(), val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.metadata.remove("_SIGNATURE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,6 +501,7 @@ impl UpdateLog {
|
||||||
version: fmri.version(),
|
version: fmri.version(),
|
||||||
catalog_parts,
|
catalog_parts,
|
||||||
signature_sha1: signature,
|
signature_sha1: signature,
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,18 +117,18 @@ pub(crate) fn write_catalog_attrs(path: &Path, attrs: &mut CatalogAttrs) -> Resu
|
||||||
#[instrument(level = "debug", skip(part))]
|
#[instrument(level = "debug", skip(part))]
|
||||||
pub(crate) fn write_catalog_part(path: &Path, part: &mut CatalogPart) -> Result<String> {
|
pub(crate) fn write_catalog_part(path: &Path, part: &mut CatalogPart) -> Result<String> {
|
||||||
// Compute signature over content without _SIGNATURE
|
// Compute signature over content without _SIGNATURE
|
||||||
part.signature = None;
|
part.set_signature(None);
|
||||||
let bytes_without_sig = serialize_python_style(&part)?;
|
let bytes_without_sig = serialize_python_style(&part)?;
|
||||||
let sig = sha1_hex(&bytes_without_sig);
|
let sig = sha1_hex(&bytes_without_sig);
|
||||||
let mut sig_map = std::collections::BTreeMap::new();
|
let mut sig_map = std::collections::BTreeMap::new();
|
||||||
sig_map.insert("sha-1".to_string(), sig);
|
sig_map.insert("sha-1".to_string(), sig);
|
||||||
part.signature = Some(sig_map);
|
part.set_signature(Some(sig_map));
|
||||||
|
|
||||||
let final_bytes = serialize_python_style(&part)?;
|
let final_bytes = serialize_python_style(&part)?;
|
||||||
debug!(path = %path.display(), bytes = final_bytes.len(), "writing catalog part");
|
debug!(path = %path.display(), bytes = final_bytes.len(), "writing catalog part");
|
||||||
atomic_write_bytes(path, &final_bytes)?;
|
atomic_write_bytes(path, &final_bytes)?;
|
||||||
Ok(part
|
Ok(part
|
||||||
.signature
|
.signature()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|m| m.get("sha-1").cloned())
|
.and_then(|m| m.get("sha-1").cloned())
|
||||||
.unwrap_or_default())
|
.unwrap_or_default())
|
||||||
|
|
@ -137,18 +137,18 @@ pub(crate) fn write_catalog_part(path: &Path, part: &mut CatalogPart) -> Result<
|
||||||
#[instrument(level = "debug", skip(log))]
|
#[instrument(level = "debug", skip(log))]
|
||||||
pub(crate) fn write_update_log(path: &Path, log: &mut UpdateLog) -> Result<String> {
|
pub(crate) fn write_update_log(path: &Path, log: &mut UpdateLog) -> Result<String> {
|
||||||
// Compute signature over content without _SIGNATURE
|
// Compute signature over content without _SIGNATURE
|
||||||
log.signature = None;
|
log.set_signature(None);
|
||||||
let bytes_without_sig = serialize_python_style(&log)?;
|
let bytes_without_sig = serialize_python_style(&log)?;
|
||||||
let sig = sha1_hex(&bytes_without_sig);
|
let sig = sha1_hex(&bytes_without_sig);
|
||||||
let mut sig_map = std::collections::BTreeMap::new();
|
let mut sig_map = std::collections::BTreeMap::new();
|
||||||
sig_map.insert("sha-1".to_string(), sig);
|
sig_map.insert("sha-1".to_string(), sig);
|
||||||
log.signature = Some(sig_map);
|
log.set_signature(Some(sig_map));
|
||||||
|
|
||||||
let final_bytes = serialize_python_style(&log)?;
|
let final_bytes = serialize_python_style(&log)?;
|
||||||
debug!(path = %path.display(), bytes = final_bytes.len(), "writing update log");
|
debug!(path = %path.display(), bytes = final_bytes.len(), "writing update log");
|
||||||
atomic_write_bytes(path, &final_bytes)?;
|
atomic_write_bytes(path, &final_bytes)?;
|
||||||
Ok(log
|
Ok(log
|
||||||
.signature
|
.signature()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|m| m.get("sha-1").cloned())
|
.and_then(|m| m.get("sha-1").cloned())
|
||||||
.unwrap_or_default())
|
.unwrap_or_default())
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue