Skip to main content

pipedash_plugin_bitbucket/
log_dialect.rs

1use pipedash_plugin_api::{
2    LineMarker,
3    LogDialect,
4    ParsedLine,
5};
6
7const COMMAND_PREFIX: &[u8] = b"+ ";
8
9pub struct BitbucketLogDialect;
10
11impl LogDialect for BitbucketLogDialect {
12    fn parse<'a>(&self, line: &'a [u8]) -> ParsedLine<'a> {
13        let is_command = line
14            .strip_prefix(COMMAND_PREFIX)
15            .is_some_and(|command| !command.trim_ascii().is_empty());
16        ParsedLine {
17            timestamp: None,
18            marker: if is_command {
19                LineMarker::Command
20            } else {
21                LineMarker::Plain
22            },
23            body: line,
24        }
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    fn parse(line: &[u8]) -> ParsedLine<'_> {
33        BitbucketLogDialect.parse(line)
34    }
35
36    #[test]
37    fn script_commands_are_commands_and_keep_their_text() {
38        let lines: [&[u8]; 5] = [
39            b"+ umask 000",
40            b"+ git reset --hard 4c684f2c6785329c62e18489ded3dbaf3f76ac0f",
41            b"+ GIT_LFS_SKIP_SMUDGE=1 retry 6 git clone --branch=\"main\" --depth 50 $BUILD_DIR",
42            b"+ echo \".bitbucket/pipelines/generated\" >> .git/info/exclude",
43            b"+ docker container run \\",
44        ];
45        for line in lines {
46            let parsed = parse(line);
47            assert_eq!(parsed.marker, LineMarker::Command, "{line:?}");
48            assert_eq!(parsed.body, line);
49            assert_eq!(parsed.timestamp, None);
50        }
51    }
52
53    #[test]
54    fn output_and_continuation_lines_stay_plain() {
55        let lines: [&[u8]; 11] = [
56            b"Cloning into '/opt/atlassian/pipelines/agent/build'...",
57            b"HEAD is now at 4c684f2 fix syntax error",
58            b"   --volume=/opt/atlassian/pipelines/agent/build:/opt/atlassian/pipelines/agent/build \\",
59            b"Default variables:",
60            b"    build : docker.io/atlassian/default-image@sha256:689e2c63",
61            b"+",
62            b"+ ",
63            b"+   ",
64            b"++ nested trace",
65            b"+added line",
66            b" + indented",
67        ];
68        for line in lines {
69            let parsed = parse(line);
70            assert_eq!(parsed.marker, LineMarker::Plain, "{line:?}");
71            assert_eq!(parsed.body, line);
72            assert_eq!(parsed.timestamp, None);
73        }
74    }
75
76    #[test]
77    fn colored_tool_output_is_not_a_command() {
78        let parsed = parse(b"\x1b[32m+ added\x1b[0m");
79        assert_eq!(parsed.marker, LineMarker::Plain);
80    }
81}