pointbreak 0.5.0

Durable terminal code review for changes humans and coding agents collaborate on together
Documentation
use std::path::Path;

use crate::error::{Result, ShoreError};
use crate::model::{ObservationId, ReviewTargetRef, Side};
use crate::session::event::{EventType, ReviewObservationRecordedPayload, ShoreEvent};
use crate::session::observation::{
    ObservationTargetSelector, ResolvedRevision, resolve_observation_target,
};

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum InputRequestTargetSelector {
    Revision,
    File {
        path: String,
    },
    Range {
        path: String,
        side: Side,
        start_line: u32,
        end_line: Option<u32>,
    },
    Observation {
        observation_id: ObservationId,
    },
}

impl InputRequestTargetSelector {
    pub fn revision() -> Self {
        Self::Revision
    }

    pub fn file(path: impl Into<String>) -> Self {
        Self::File { path: path.into() }
    }

    pub fn range(
        path: impl Into<String>,
        side: Side,
        start_line: u32,
        end_line: Option<u32>,
    ) -> Self {
        Self::Range {
            path: path.into(),
            side,
            start_line,
            end_line,
        }
    }

    pub fn observation(observation_id: ObservationId) -> Self {
        Self::Observation { observation_id }
    }
}

pub(super) fn resolve_input_request_target(
    repo: &Path,
    events: &[ShoreEvent],
    resolved: &ResolvedRevision,
    selector: &InputRequestTargetSelector,
) -> Result<ReviewTargetRef> {
    match selector {
        InputRequestTargetSelector::Revision => {
            resolve_observation_target(repo, resolved, &ObservationTargetSelector::revision())
        }
        InputRequestTargetSelector::File { path } => {
            resolve_observation_target(repo, resolved, &ObservationTargetSelector::file(path))
        }
        InputRequestTargetSelector::Range {
            path,
            side,
            start_line,
            end_line,
        } => resolve_observation_target(
            repo,
            resolved,
            &ObservationTargetSelector::range(path, *side, *start_line, *end_line),
        ),
        InputRequestTargetSelector::Observation { observation_id } => {
            resolve_native_observation_target(events, resolved, observation_id)
        }
    }
}

fn resolve_native_observation_target(
    events: &[ShoreEvent],
    resolved: &ResolvedRevision,
    observation_id: &ObservationId,
) -> Result<ReviewTargetRef> {
    for event in events
        .iter()
        .filter(|event| event.event_type == EventType::ReviewObservationRecorded)
    {
        if event.subject_revision_id()?.as_ref() != Some(&resolved.revision_id) {
            continue;
        }

        let payload: ReviewObservationRecordedPayload =
            serde_json::from_value(event.payload.clone())?;
        if &payload.observation_id == observation_id {
            return Ok(ReviewTargetRef::Observation {
                revision_id: resolved.revision_id.clone(),
                observation_id: observation_id.clone(),
            });
        }
    }

    Err(ShoreError::Message(format!(
        "unknown observation target: {}",
        observation_id.as_str()
    )))
}