bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

pub const OBJECT_LAYOUT_WHOLE_OBJECT: &str = "whole-object";
pub const OBJECT_LAYOUT_CHUNKED: &str = "chunked";
pub const OBJECT_LAYOUT_STRIPED: &str = "striped";
pub const OBJECT_LAYOUT_PACKED_SMALL_OBJECTS: &str = "packed-small-objects";
pub const OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS: &str = "append-only-segments";

const OBJECT_LAYOUT_CAPABILITIES: &[&str] = &[
    "whole-object-layout",
    "chunked-layout",
    "striped-layout",
    "packed-small-objects",
    "append-only-segments",
    "native-support-state",
    "semantic-parity",
    "configuration-admin-surface",
    "security-governance-impact",
    "observability-evidence",
    "failure-mode-behavior",
    "validation-test-coverage",
    "product-specific-caveats",
];

const OBJECT_LAYOUT_CAVEATS: &[&str] = &[
    "BucketWarden supports whole-object committed versions and multipart chunk assembly for the local runtime boundary.",
    "Chunked layout proof is the S3 multipart path: ordered parts are assembled atomically into one committed object version.",
    "Striped layout, packed small-object containers, and append-only segment stores are tracked but fail closed until they have runtime semantics.",
    "Object layout proof does not claim distributed striping, small-object packing compaction, or log-segment garbage collection.",
];

const OBJECT_LAYOUT_FAILURE_MODES: &[&str] = &[
    "unsupported-object-layout-rejected",
    "invalid-chunk-size-rejected",
    "striped-layout-policy-rejected",
    "packed-small-object-policy-rejected",
    "append-only-segment-policy-rejected",
];

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct ObjectLayoutSupportEntry {
    pub layout: &'static str,
    pub native_support: bool,
    pub semantic_parity: &'static str,
    pub write_path: &'static str,
    pub read_path: &'static str,
    pub failure_mode: &'static str,
    pub caveat: &'static str,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct ObjectLayoutSupportReport {
    pub active_layout: &'static str,
    pub supported_layouts: Vec<&'static str>,
    pub unsupported_layouts: Vec<&'static str>,
    pub default_chunk_policy: &'static str,
    pub capabilities: Vec<&'static str>,
    pub failure_modes: Vec<&'static str>,
    pub caveats: Vec<&'static str>,
    pub entries: Vec<ObjectLayoutSupportEntry>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
pub struct ObjectLayoutPolicy {
    pub layout: String,
    pub chunk_size_bytes: Option<usize>,
    pub stripe_width: Option<u8>,
    pub pack_small_objects: bool,
    pub append_only_segments: bool,
}

impl BucketWarden {
    pub fn object_layout_support_report(&self) -> ObjectLayoutSupportReport {
        ObjectLayoutSupportReport {
            active_layout: OBJECT_LAYOUT_WHOLE_OBJECT,
            supported_layouts: vec![OBJECT_LAYOUT_WHOLE_OBJECT, OBJECT_LAYOUT_CHUNKED],
            unsupported_layouts: vec![
                OBJECT_LAYOUT_STRIPED,
                OBJECT_LAYOUT_PACKED_SMALL_OBJECTS,
                OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS,
            ],
            default_chunk_policy: "multipart-ordered-part-assembly",
            capabilities: OBJECT_LAYOUT_CAPABILITIES.to_vec(),
            failure_modes: OBJECT_LAYOUT_FAILURE_MODES.to_vec(),
            caveats: OBJECT_LAYOUT_CAVEATS.to_vec(),
            entries: vec![
                ObjectLayoutSupportEntry {
                    layout: OBJECT_LAYOUT_WHOLE_OBJECT,
                    native_support: true,
                    semantic_parity: "Single PUT writes one committed object version with body, metadata, lock, and encryption envelope.",
                    write_path: "put_object commits the full object body atomically as one visible version.",
                    read_path: "get_object and range reads return the committed whole-object bytes.",
                    failure_mode: "Invalid object keys, digest mismatches, and lock conflicts reject before commit.",
                    caveat: "Whole-object layout is local runtime behavior and does not claim remote block placement.",
                },
                ObjectLayoutSupportEntry {
                    layout: OBJECT_LAYOUT_CHUNKED,
                    native_support: true,
                    semantic_parity: "Multipart uploads preserve ordered chunks and commit them into one visible object version.",
                    write_path: "create_multipart_upload, upload_part, and complete_multipart_upload assemble ordered parts.",
                    read_path: "reads observe the assembled object body after multipart completion.",
                    failure_mode: "Missing parts, invalid part numbers, and ETag mismatches reject completion.",
                    caveat: "Chunked support is S3 multipart assembly, not distributed chunk placement.",
                },
                ObjectLayoutSupportEntry {
                    layout: OBJECT_LAYOUT_STRIPED,
                    native_support: false,
                    semantic_parity: "No cross-disk or cross-node stripe placement semantics are claimed.",
                    write_path: "striped writes are out of the current runtime boundary.",
                    read_path: "striped reads are out of the current runtime boundary.",
                    failure_mode: "Striped layout selection is rejected as unsupported.",
                    caveat: "Striping needs explicit shard placement and reconstruction contracts.",
                },
                ObjectLayoutSupportEntry {
                    layout: OBJECT_LAYOUT_PACKED_SMALL_OBJECTS,
                    native_support: false,
                    semantic_parity: "No packed container, offset index, or compaction semantics are claimed.",
                    write_path: "packed small-object writes are out of the current runtime boundary.",
                    read_path: "packed small-object reads are out of the current runtime boundary.",
                    failure_mode: "Packed small-object layout selection is rejected as unsupported.",
                    caveat: "Packed small-object support needs container index and compaction proof.",
                },
                ObjectLayoutSupportEntry {
                    layout: OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS,
                    native_support: false,
                    semantic_parity: "No append-only object segment log or segment cleaner semantics are claimed.",
                    write_path: "append-only segment writes are out of the current runtime boundary.",
                    read_path: "append-only segment reads are out of the current runtime boundary.",
                    failure_mode: "Append-only segment layout selection is rejected as unsupported.",
                    caveat: "Append-only segment support needs segment index and recovery proof.",
                },
            ],
        }
    }

    pub fn ensure_object_layout_supported(&self, layout: &str) -> Result<(), RuntimeError> {
        let report = self.object_layout_support_report();
        if report.supported_layouts.contains(&layout) {
            Ok(())
        } else {
            Err(RuntimeError::UnsupportedObjectLayout(layout.to_string()))
        }
    }

    pub fn validate_object_layout_policy(
        &self,
        policy: &ObjectLayoutPolicy,
    ) -> Result<(), RuntimeError> {
        self.ensure_object_layout_supported(&policy.layout)?;
        if policy.stripe_width.is_some_and(|width| width > 1) {
            return Err(RuntimeError::InvalidObjectLayoutPolicy(
                "striped object layout is outside the current layout boundary".to_string(),
            ));
        }
        if policy.pack_small_objects {
            return Err(RuntimeError::InvalidObjectLayoutPolicy(
                "packed small-object layout is outside the current layout boundary".to_string(),
            ));
        }
        if policy.append_only_segments {
            return Err(RuntimeError::InvalidObjectLayoutPolicy(
                "append-only segment layout is outside the current layout boundary".to_string(),
            ));
        }
        if matches!(policy.layout.as_str(), OBJECT_LAYOUT_CHUNKED)
            && policy.chunk_size_bytes.is_some_and(|bytes| bytes == 0)
        {
            return Err(RuntimeError::InvalidObjectLayoutPolicy(
                "chunked object layout requires a nonzero chunk size".to_string(),
            ));
        }
        Ok(())
    }
}