jj-cz 1.1.0

Conventional commits for Jujutsu
Documentation
use super::Footer;

#[repr(transparent)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct References(Vec<String>);

impl<T> From<T> for References
where
    T: ToString,
{
    fn from(value: T) -> Self {
        let references: Vec<String> = value
            .to_string()
            .split(",")
            .flat_map(|e| match e.trim() {
                "" => None,
                e => Some(e.to_string()),
            })
            .collect();
        Self(references)
    }
}

impl Footer for References {
    fn prefix(&self) -> &str {
        "Refs: "
    }

    fn note(&self) -> &str {
        ""
    }

    fn as_footer(&self) -> String {
        if self.0.is_empty() {
            String::new()
        } else {
            let footers: Vec<String> = self
                .0
                .iter()
                .map(|r| format!("{}{r}\n", self.prefix()))
                .collect();
            footers.join("")
        }
    }
}

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

    /// Default is empty
    #[test]
    fn default_is_empty() {
        let refs = References::default();
        assert!(refs.0.is_empty());
    }

    /// Empty input produces empty references
    #[test]
    fn from_empty_string() {
        assert_eq!(References::from(""), References::default());
    }

    /// Whitespace-only input produces empty references
    #[test]
    fn from_whitespace_only() {
        assert_eq!(References::from("   "), References::default());
    }

    /// Single reference without commas
    #[test]
    fn from_single_reference() {
        let refs = References::from("#123");
        assert_eq!(refs.0, vec!["#123".to_string()]);
    }

    /// Comma-separated references are split and trimmed
    #[test]
    fn from_comma_separated() {
        let refs = References::from("#123, #456");
        assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
    }

    /// Leading whitespace around references is trimmed
    #[test]
    fn from_trims_leading_whitespace() {
        let refs = References::from("  #123,   #456");
        assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
    }

    /// Trailing whitespace around references is trimmed
    #[test]
    fn from_trims_trailing_whitespace() {
        let refs = References::from("#123  , #456  ");
        assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
    }

    /// Empty segments from consecutive commas are filtered out
    #[test]
    fn from_filters_empty_segments() {
        let refs = References::from("#123,,, #456");
        assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
    }

    /// From works with owned String
    #[test]
    fn from_owned_string() {
        let input = "#123, #456".to_string();
        let refs = References::from(input);
        assert_eq!(refs.0, vec!["#123".to_string(), "#456".to_string()]);
    }

    /// as_footer returns empty string for empty references
    #[test]
    fn as_footer_empty() {
        let refs = References::default();
        assert_eq!(refs.as_footer(), "");
    }

    /// as_footer returns single line for one reference
    #[test]
    fn as_footer_single() {
        let refs = References::from("#123");
        assert_eq!(refs.as_footer(), "Refs: #123\n");
    }

    /// as_footer returns multiple lines for multiple references
    #[test]
    fn as_footer_multiple() {
        let refs = References::from("#123, #456");
        assert_eq!(refs.as_footer(), "Refs: #123\nRefs: #456\n");
    }

    /// as_footer handles Jira-style references
    #[test]
    fn as_footer_jira_style() {
        let refs = References::from("OPS-456, PROJ-789");
        assert_eq!(refs.as_footer(), "Refs: OPS-456\nRefs: PROJ-789\n");
    }

    /// Footer trait prefix returns correct value
    #[test]
    fn footer_prefix() {
        let refs = References::default();
        assert_eq!(refs.prefix(), "Refs: ");
    }

    /// Footer trait note returns empty string
    #[test]
    fn footer_note() {
        let refs = References::default();
        assert_eq!(refs.note(), "");
    }

    /// Clone produces equal value
    #[test]
    fn clone_equality() {
        let refs = References::from("#123, #456");
        let cloned = refs.clone();
        assert_eq!(refs, cloned);
    }

    /// Debug output is available
    #[test]
    fn debug_output() {
        let refs = References::from("#123");
        let debug = format!("{:?}", refs);
        assert!(debug.contains("References"));
    }

    /// Different references are not equal
    #[test]
    fn inequality_different_refs() {
        let a = References::from("#123");
        let b = References::from("#456");
        assert_ne!(a, b);
    }

    /// Empty vs non-empty are not equal
    #[test]
    fn inequality_empty_vs_non_empty() {
        let empty = References::default();
        let non_empty = References::from("#123");
        assert_ne!(empty, non_empty);
    }
}