summaryrefslogtreecommitdiff
path: root/lib/MaildirIndexer/Parser.rakumod
blob: 420d4c320d51666fd7bdf67359323316ecdc1672 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use v6.d;
unit module MaildirIndexer::Parser;
use MaildirIndexer::LogTimelineSchema;
use MaildirIndexer::Email;
 
my @separators = (
    "\x0a\x0d\x0a\x0d",
    "\x0d\x0a\x0d\x0a",
    "\x0a\x0a",
    "\x0d\x0d",
);
 
my grammar Message {
    regex TOP {
        <headers>
        <separator>
        <body>
    }
 
    token newline { [\x0d\x0a] | [\x0a\x0d] | \x0a | \x0d }
    token separator { @separators }
 
    token body { .* }
    regex headers {
        <header>+ % <newline>
    }
    regex header {
        <name> \: \h* <value>
                 || <junk>
    }
    token name {
        <-[:\s]>+
    }
    regex value {
        <line>+ % [<newline> \h+]
    }
    token line { \N* }
    token junk { \N+ }
}
 
my class Message-actions {
    has $.path = IO;
    method TOP($/{
        make MaildirIndexer::Email.new(
            headers => $/<headers>.made,
            body => $/<body>.Str,
            path => $.path,
        );
    }
    method headers($/{
        make %flat |$/<header>».made );
    }
    method header($/{
        make $/<junk> ?? () !! ( $/<name>.Str.lc => $/<value>.made );
    }
    method value($/{
        make $/<line>.join(' ')
    }
}
 
multi parse-email(IO::Path:D $p --> MaildirIndexer::Emailis export {
    my MaildirIndexer::Email $result;
    MaildirIndexer::LogTimelineSchema::Parse::Email::File.log: :file($p.path), -> {
        $result = parse-email($p.slurp(:enc<utf8-c8>), path => $p);
    }
    return $result;
}
 
multi parse-email(IO::Path:D $p:$headers-only! --> MaildirIndexer::Emailis export {
    my MaildirIndexer::Email $result;
    MaildirIndexer::LogTimelineSchema::Parse::Email::File.log: :file($p.path), -> {
        my IO::Handle $h = $p.open(
                :enc<utf8-c8>,
                :nl-in(@separators),
                :!chomp,
        );
        $result = parse-email(
            $h.lines()[0],
            path => $p,
        );
        $h.close();
    }
    return $result;
}
 
multi parse-email(IO::Socket::Async:D $s --> MaildirIndexer::Emailis export {
    my MaildirIndexer::Email $result;
    MaildirIndexer::LogTimelineSchema::Parse::Email::Socket.log: -> {
        my $string;
        react {
            whenever $s.Supply(:enc<utf8-c8>{
                $string ~= $_;
                # parsing the whole email is much faster (0.02 seconds 
                # instead of 1.2!) than just C<< $string ~~ 
                # /@separators/ >> 
                $result = parse-email($stringand done;
            }
        }
    }
    return $result;
}
 
multi parse-email(Str:D $email-str:$path = IO --> MaildirIndexer::Emailis export {
    my MaildirIndexer::Email $result;
    MaildirIndexer::LogTimelineSchema::Parse::Email::Str.log: -> {
        CATCH { warn $_return Nil };
        with Message.parse($email-str,:actions(Message-actions.new(:$path))) {
            $result = .made;
        }
    }
    return $result;
}