jk-core 0.2.1

Core state and integration contracts for jk
Documentation
//! Shared log records used by `jk` integration and TUI crates.
//!
//! `jk-core` keeps the boundary between opaque `jj` rendering and semantic log state explicit. The
//! rendered log body is still owned by `jj`; `jk` only keeps enough structured metadata to move by
//! change, preserve selection across refresh, and insert inline details at the right rendered line.

/// A rendered `jj` log view plus semantic records for navigation.
///
/// The `rendered` body is opaque terminal text produced by `jj`. Each [`LogEntry`] in `entries`
/// must correspond to one commit row in that rendered text through [`LogEntry::rendered_line`].
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LogSnapshot {
    title: String,
    rendered: String,
    entries: Vec<LogEntry>,
}

impl LogSnapshot {
    /// Creates a log snapshot from rendered terminal text and semantic entries.
    ///
    /// The caller is responsible for ensuring each entry's rendered line points at the matching
    /// commit row in `rendered`.
    #[must_use]
    pub fn new(rendered: impl Into<String>, entries: Vec<LogEntry>) -> Self {
        Self {
            title: String::new(),
            rendered: rendered.into(),
            entries,
        }
    }

    /// Sets the command context shown in the title bar.
    #[must_use]
    pub fn with_title(mut self, title: impl Into<String>) -> Self {
        self.title = title.into();
        self
    }

    /// Returns the human-readable command context for the current view.
    #[must_use]
    pub fn title(&self) -> &str {
        &self.title
    }

    /// Returns the opaque output rendered by `jj`.
    #[must_use]
    pub fn rendered(&self) -> &str {
        &self.rendered
    }

    /// Returns semantic records aligned to the rendered log body.
    #[must_use]
    pub fn entries(&self) -> &[LogEntry] {
        &self.entries
    }

    /// Consumes the snapshot into its owned title, rendered body, and entries.
    #[must_use]
    pub fn into_parts(self) -> (String, String, Vec<LogEntry>) {
        (self.title, self.rendered, self.entries)
    }
}

/// A semantic log item that the TUI can navigate without knowing how `jj` produced it.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LogEntry {
    change_id: String,
    commit_id: String,
    description: String,
    details: String,
    rendered_line: usize,
}

impl LogEntry {
    /// Creates a log entry from `jj` change metadata.
    ///
    /// New entries default to rendered line `0`; callers that build a [`LogSnapshot`] should set
    /// the aligned rendered line with [`Self::with_rendered_line`] while constructing entries.
    #[must_use]
    pub fn new(
        change_id: impl Into<String>,
        commit_id: impl Into<String>,
        description: impl Into<String>,
    ) -> Self {
        Self {
            change_id: change_id.into(),
            commit_id: commit_id.into(),
            description: description.into(),
            details: String::new(),
            rendered_line: 0,
        }
    }

    /// Sets the template-derived detail text for inline expansion.
    #[must_use]
    pub fn with_details(mut self, details: impl Into<String>) -> Self {
        self.details = details.into();
        self
    }

    /// Sets the rendered line for this entry.
    #[must_use]
    pub const fn with_rendered_line(mut self, rendered_line: usize) -> Self {
        self.rendered_line = rendered_line;
        self
    }

    /// Returns the stable `jj` change identifier.
    #[must_use]
    pub fn change_id(&self) -> &str {
        &self.change_id
    }

    /// Returns the commit identifier for follow-up inspection commands.
    #[must_use]
    pub fn commit_id(&self) -> &str {
        &self.commit_id
    }

    /// Returns the full commit description.
    #[must_use]
    pub fn description(&self) -> &str {
        &self.description
    }

    /// Returns template-derived detail text for inline expansion.
    #[must_use]
    pub fn details(&self) -> &str {
        &self.details
    }

    /// Returns the zero-based line in the rendered `jj` output for this entry.
    #[must_use]
    pub const fn rendered_line(&self) -> usize {
        self.rendered_line
    }

    /// Returns the first line of the commit description, or a placeholder for empty descriptions.
    #[must_use]
    pub fn summary(&self) -> &str {
        let first_line = self.description.lines().next().unwrap_or_default();
        if first_line.is_empty() {
            "(no description set)"
        } else {
            first_line
        }
    }
}