view rust/hg-core/src/revlog/changelog.rs @ 49120:fb82b5cb8301

rust-changelog: don't skip empty lines when iterating over changeset lines The first empty line in the changeset indicates the end of headers and beginning of description. Callers can't know figure out where that position is if empty lines are skipped. Differential Revision: https://phab.mercurial-scm.org/D12426
author Martin von Zweigbergk <martinvonz@google.com>
date Thu, 31 Mar 2022 22:06:26 -0700
parents f2f57724d4eb
children cc132255261b
line wrap: on
line source

use crate::errors::HgError;
use crate::repo::Repo;
use crate::revlog::node::NULL_NODE;
use crate::revlog::revlog::{Revlog, RevlogError};
use crate::revlog::Revision;
use crate::revlog::{Node, NodePrefix};

/// A specialized `Revlog` to work with `changelog` data format.
pub struct Changelog {
    /// The generic `revlog` format.
    pub(crate) revlog: Revlog,
}

impl Changelog {
    /// Open the `changelog` of a repository given by its root.
    pub fn open(repo: &Repo) -> Result<Self, HgError> {
        let revlog = Revlog::open(repo, "00changelog.i", None)?;
        Ok(Self { revlog })
    }

    /// Return the `ChangelogEntry` for the given node ID.
    pub fn data_for_node(
        &self,
        node: NodePrefix,
    ) -> Result<ChangelogRevisionData, RevlogError> {
        let rev = self.revlog.rev_from_node(node)?;
        self.data_for_rev(rev)
    }

    /// Return the `ChangelogEntry` of the given revision number.
    pub fn data_for_rev(
        &self,
        rev: Revision,
    ) -> Result<ChangelogRevisionData, RevlogError> {
        let bytes = self.revlog.get_rev_data(rev)?.into_owned();
        Ok(ChangelogRevisionData { bytes })
    }

    pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
        self.revlog.node_from_rev(rev)
    }
}

/// `Changelog` entry which knows how to interpret the `changelog` data bytes.
#[derive(Debug)]
pub struct ChangelogRevisionData {
    /// The data bytes of the `changelog` entry.
    bytes: Vec<u8>,
}

impl ChangelogRevisionData {
    /// Return an iterator over the lines of the entry.
    pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
        self.bytes.split(|b| b == &b'\n')
    }

    /// Return the node id of the `manifest` referenced by this `changelog`
    /// entry.
    pub fn manifest_node(&self) -> Result<Node, HgError> {
        let manifest_node_hex =
            self.lines().next().expect("Empty iterator from split()?");
        if manifest_node_hex.is_empty() {
            Ok(NULL_NODE)
        } else {
            Node::from_hex_for_repo(manifest_node_hex)
        }
    }
}