package Sietima::MailStore::FS;
use Moo;
use Sietima::Policy;
use Types::Path::Tiny qw(Dir);
use Types::Standard qw(Object ArrayRef Str Slurpy);
use Type::Params -sigs;
use Sietima::Types qw(EmailMIME TagName);
use Digest::SHA qw(sha1_hex);
use namespace::clean;
our $VERSION = '1.1.0';
with 'Sietima::MailStore';
has root => (
is => 'ro',
required => 1,
isa => Dir,
coerce => 1,
);
has [qw(_tagdir _msgdir)] => ( is => 'lazy' );
sub _build__tagdir($self) { $self->root->child('tags') }
sub _build__msgdir($self) { $self->root->child('msgs') }
sub BUILD($self,@) {
$self->$_->mkpath for qw(_tagdir _msgdir);
return;
}
signature_for store => (
method => Object,
positional => [
EmailMIME,
Slurpy[ArrayRef[TagName]],
],
);
sub store($self,$mail,$tags) {
my $str = $mail->as_string;
my $id = sha1_hex($str);
$self->_msgdir->child($id)->spew_raw($str);
$self->_tagdir->child($_)->append("$id\n") for $tags->@*;
return $id;
}
signature_for retrieve_by_id => (
method => Object,
positional => [ Str ],
);
sub retrieve_by_id($self,$id) {
my $msg_path = $self->_msgdir->child($id);
return unless -e $msg_path;
return Email::MIME->new($msg_path->slurp_raw);
}
sub _tagged_by($self,$tag) {
my $tag_file = $self->_tagdir->child($tag);
return unless -e $tag_file;
return $tag_file->lines({chomp=>1});
}
signature_for retrieve_ids_by_tags => (
method => Object,
positional => [
Slurpy[ArrayRef[TagName]],
],
);
sub retrieve_ids_by_tags($self,$tags) {
my %msgs;
if ($tags->@*) {
for my $tag ($tags->@*) {
$_++ for @msgs{$self->_tagged_by($tag)};
}
}
else {
$msgs{$_->basename}=0 for $self->_msgdir->children;
}
my @ret;
for my $id (keys %msgs) {
next unless $msgs{$id} == $tags->@*;
push @ret, $id;
}
return \@ret;
}
signature_for retrieve_by_tags => (
method => Object,
positional => [
Slurpy[ArrayRef[TagName]],
],
);
sub retrieve_by_tags($self,$tags) {
my @ret;
for my $id ($self->retrieve_ids_by_tags($tags->@*)->@*) {
push @ret, {
id => $id,
mail => $self->retrieve_by_id($id),
};
}
return \@ret;
}
signature_for remove => (
method => Object,
positional => [ Str ],
);
sub remove($self,$id) {
for my $tag_file ($self->_tagdir->children) {
$tag_file->edit_lines( sub { $_='' if /\A\Q$id\E\n?\z/ } );
}
$self->_msgdir->child($id)->remove;
return;
}
sub clear($self) {
do { $self->$_->remove_tree;$self->$_->mkpath } for qw(_tagdir _msgdir);
return;
}
1;
__END__