wisp-core 0.1.0

core domain model and projections for Wisp
Documentation
use std::path::PathBuf;

use crate::{Candidate, CandidateMetadata};

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PreviewKey {
    Session(String),
    Directory(PathBuf),
    File(PathBuf),
    Metadata(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PreviewKind {
    SessionSummary,
    Directory,
    File,
    Metadata,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PreviewRequest {
    SessionSummary {
        key: PreviewKey,
        session_name: String,
    },
    Directory {
        key: PreviewKey,
        path: PathBuf,
    },
    File {
        key: PreviewKey,
        path: PathBuf,
    },
    Metadata {
        key: PreviewKey,
        title: String,
    },
}

impl PreviewRequest {
    #[must_use]
    pub fn key(&self) -> &PreviewKey {
        match self {
            Self::SessionSummary { key, .. }
            | Self::Directory { key, .. }
            | Self::File { key, .. }
            | Self::Metadata { key, .. } => key,
        }
    }

    #[must_use]
    pub fn kind(&self) -> PreviewKind {
        match self {
            Self::SessionSummary { .. } => PreviewKind::SessionSummary,
            Self::Directory { .. } => PreviewKind::Directory,
            Self::File { .. } => PreviewKind::File,
            Self::Metadata { .. } => PreviewKind::Metadata,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PreviewContent {
    pub title: String,
    pub body: Vec<String>,
    pub truncated: bool,
}

impl PreviewContent {
    #[must_use]
    pub fn from_text(title: impl Into<String>, text: impl AsRef<str>, max_lines: usize) -> Self {
        let mut body = text
            .as_ref()
            .lines()
            .take(max_lines)
            .map(ToOwned::to_owned)
            .collect::<Vec<_>>();
        let truncated = text.as_ref().lines().count() > max_lines;

        if body.is_empty() {
            body.push(String::new());
        }

        Self {
            title: title.into(),
            body,
            truncated,
        }
    }

    #[must_use]
    pub fn from_text_tail(
        title: impl Into<String>,
        text: impl AsRef<str>,
        max_lines: usize,
    ) -> Self {
        let lines = text
            .as_ref()
            .lines()
            .map(ToOwned::to_owned)
            .collect::<Vec<_>>();
        let truncated = lines.len() > max_lines;
        let start = lines.len().saturating_sub(max_lines);
        let mut body = lines.into_iter().skip(start).collect::<Vec<_>>();

        if body.is_empty() {
            body.push(String::new());
        }

        Self {
            title: title.into(),
            body,
            truncated,
        }
    }
}

#[must_use]
pub fn preview_request_for_candidate(candidate: &Candidate) -> PreviewRequest {
    match &candidate.metadata {
        CandidateMetadata::Session(metadata) => PreviewRequest::SessionSummary {
            key: candidate.preview_key.clone(),
            session_name: metadata.session_name.clone(),
        },
        CandidateMetadata::Directory(metadata) => PreviewRequest::Directory {
            key: candidate.preview_key.clone(),
            path: metadata.full_path.clone(),
        },
        CandidateMetadata::Window(metadata) => PreviewRequest::Metadata {
            key: candidate.preview_key.clone(),
            title: format!("{}:{}", metadata.session_name, metadata.index),
        },
        CandidateMetadata::Project(metadata) => PreviewRequest::Directory {
            key: candidate.preview_key.clone(),
            path: metadata.root.clone(),
        },
        CandidateMetadata::Worktree(metadata) => PreviewRequest::Directory {
            key: candidate.preview_key.clone(),
            path: metadata.full_path.clone(),
        },
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use crate::{
        Candidate, PreviewKey, PreviewRequest, WorktreeMetadata, preview_request_for_candidate,
    };

    #[test]
    fn maps_worktree_candidates_to_directory_previews_using_full_path() {
        let full_path = PathBuf::from("/tmp/demo-worktree");
        let candidate = Candidate::worktree(WorktreeMetadata {
            full_path: full_path.clone(),
            display_path: "~/demo-worktree".to_string(),
            branch: Some("feature/demo".to_string()),
        });

        let request = preview_request_for_candidate(&candidate);

        assert_eq!(
            request,
            PreviewRequest::Directory {
                key: PreviewKey::Directory(full_path.clone()),
                path: full_path,
            }
        );
    }
}