use super::{
IcSnapshotUploadError, IcSnapshotUploadKind, IcSnapshotUploadRequest,
MAX_IC_SNAPSHOT_UPLOAD_REPLY_BYTES, validate_destination,
};
use crate::model::{
artifacts::ArtifactChecksumRecord,
ic_lifecycle_reply::EMPTY_CANDID_REPLY,
ic_snapshot_reply::snapshot_id,
ic_wire::{self, DecoderLimits},
};
use candid::CandidType;
use serde::Deserialize;
use std::fmt;
#[derive(CandidType, Deserialize)]
struct WireMetadataReply {
#[serde(deserialize_with = "snapshot_id")]
snapshot_id: Vec<u8>,
}
#[derive(Debug, Eq, PartialEq)]
pub enum IcSnapshotUploadReplyKind {
Metadata {
snapshot_id: Vec<u8>,
},
DataAcknowledgement,
}
pub struct IcSnapshotUploadReply<'request, 'source> {
request: &'request IcSnapshotUploadRequest<'source>,
kind: IcSnapshotUploadReplyKind,
payload_checksum: ArtifactChecksumRecord,
}
impl<'request, 'source> IcSnapshotUploadReply<'request, 'source> {
pub fn decode(
request: &'request IcSnapshotUploadRequest<'source>,
bytes: &[u8],
) -> Result<Self, IcSnapshotUploadError> {
if bytes.len() > MAX_IC_SNAPSHOT_UPLOAD_REPLY_BYTES {
return Err(IcSnapshotUploadError::ReplyTooLarge);
}
let kind = match request.kind() {
IcSnapshotUploadKind::Metadata => {
let reply: WireMetadataReply = ic_wire::decode_one(
bytes,
DecoderLimits {
decoding: 64 * 1024,
skipping: 0,
type_table: 16,
header: Some(4096),
},
)
.map_err(|()| IcSnapshotUploadError::InvalidReply)?;
validate_destination(request.source(), &reply.snapshot_id)?;
IcSnapshotUploadReplyKind::Metadata {
snapshot_id: reply.snapshot_id,
}
}
IcSnapshotUploadKind::Data { .. } => {
if bytes != EMPTY_CANDID_REPLY {
return Err(IcSnapshotUploadError::InvalidReply);
}
IcSnapshotUploadReplyKind::DataAcknowledgement
}
};
Ok(Self {
request,
kind,
payload_checksum: ArtifactChecksumRecord::from_bytes(bytes),
})
}
#[must_use]
pub const fn request(&self) -> &'request IcSnapshotUploadRequest<'source> {
self.request
}
#[must_use]
pub const fn kind(&self) -> &IcSnapshotUploadReplyKind {
&self.kind
}
#[must_use]
pub const fn payload_checksum(&self) -> &ArtifactChecksumRecord {
&self.payload_checksum
}
#[must_use]
pub fn digest(&self) -> ArtifactChecksumRecord {
let mut bytes = b"ic-backup/ic-snapshot-upload-reply/v1\0".to_vec();
bytes.extend_from_slice(self.request.binding_digest().hash().as_bytes());
bytes.extend_from_slice(self.payload_checksum.hash().as_bytes());
ArtifactChecksumRecord::from_bytes(&bytes)
}
}
impl fmt::Debug for IcSnapshotUploadReply<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IcSnapshotUploadReply")
.field("method", &self.request.method())
.field("payload_checksum", &self.payload_checksum)
.finish_non_exhaustive()
}
}