Skip to main content

dk_core/
lib.rs

1/// The version of the dk-core crate (set at compile time).
2pub const VERSION: &str = env!("CARGO_PKG_VERSION");
3
4pub mod error;
5pub mod types;
6
7pub use error::{Error, Result};
8pub use types::*;
9
10// ── Git author helpers ──
11
12/// Strip characters that would corrupt a raw git commit-object header.
13/// Removes null bytes, newlines, and angle brackets (git author/email delimiters).
14fn sanitize_author_field(s: &str) -> String {
15    s.chars()
16        .filter(|c| !matches!(c, '\0' | '\n' | '\r' | '<' | '>'))
17        .collect()
18}
19
20/// Resolve the effective git author name and email for a merge commit.
21/// Falls back to the agent identity when the caller supplies empty or
22/// all-stripped strings. Sanitization runs BEFORE the emptiness check
23/// so that inputs like "\n" correctly fall back to the agent identity.
24pub fn resolve_author(name: &str, email: &str, agent: &str) -> (String, String) {
25    let safe_agent = sanitize_author_field(agent);
26    let sanitized_name = sanitize_author_field(name);
27    let effective_name = if sanitized_name.is_empty() {
28        safe_agent.clone()
29    } else {
30        sanitized_name
31    };
32    let sanitized_email = sanitize_author_field(email);
33    let effective_email = if sanitized_email.is_empty() {
34        format!("{}@dkod.dev", safe_agent)
35    } else {
36        sanitized_email
37    };
38    (effective_name, effective_email)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn resolve_author_uses_supplied_values() {
47        let (name, email) = resolve_author("Alice", "alice@example.com", "agent-1");
48        assert_eq!(name, "Alice");
49        assert_eq!(email, "alice@example.com");
50    }
51
52    #[test]
53    fn resolve_author_falls_back_to_agent() {
54        let (name, email) = resolve_author("", "", "agent-1");
55        assert_eq!(name, "agent-1");
56        assert_eq!(email, "agent-1@dkod.dev");
57    }
58
59    #[test]
60    fn resolve_author_sanitizes_newlines_and_nulls() {
61        let (name, email) = resolve_author("Al\nice\0", "al\rice@\nex.com", "agent-1");
62        assert_eq!(name, "Alice");
63        assert_eq!(email, "alice@ex.com");
64    }
65
66    #[test]
67    fn resolve_author_falls_back_when_input_is_only_stripped_chars() {
68        let (name, email) = resolve_author("\n", "\r\0", "agent-1");
69        assert_eq!(name, "agent-1");
70        assert_eq!(email, "agent-1@dkod.dev");
71    }
72
73    #[test]
74    fn resolve_author_strips_angle_brackets() {
75        let (name, email) = resolve_author("Alice <hacker>", "a<b>c@ex.com", "agent-1");
76        assert_eq!(name, "Alice hacker");
77        assert_eq!(email, "abc@ex.com");
78    }
79}