/// Segment-local document identifier.
///
/// Range: `0..max_doc-1` within a segment. All index structures within a
/// segment share this ID space — see [[architecture-segment-layout]].
///
/// `Copy` because DocIds are passed by value constantly in the hot scoring
/// loop. Public inner field because callers frequently need the raw `u32`
/// for array indexing, bitset operations, and serialization.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DocId(pub u32);

/// Sentinel value indicating no more matching documents.
///
/// Guaranteed to be greater than any valid `DocId`. This means the maximum
/// valid `doc_count` per segment is `u32::MAX - 1` (~4.29 billion) — see
/// [[architecture-segment-layout#Document Count Fields]].
pub const NO_MORE_DOCS: DocId = DocId(u32::MAX);

impl DocId {
    pub const fn new(id: u32) -> Self {
        Self(id)
    }

    pub const fn as_u32(self) -> u32 {
        self.0
    }
}

impl std::fmt::Display for DocId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn no_more_docs_is_greater_than_any_valid_doc_id() {
        assert!(NO_MORE_DOCS > DocId(0));
        assert!(NO_MORE_DOCS > DocId(u32::MAX - 1));
    }

    #[test]
    fn ordering() {
        assert!(DocId(0) < DocId(1));
        assert!(DocId(1) < DocId(100));
        assert!(DocId(100) < DocId(u32::MAX - 1));
    }

    #[test]
    fn as_u32_round_trips() {
        assert_eq!(DocId::new(42).as_u32(), 42);
        assert_eq!(DocId(0).as_u32(), 0);
    }

    #[test]
    fn display() {
        assert_eq!(format!("{}", DocId(123)), "123");
    }
}
