[geary/mjog/mail-merge-plugin: 1/10] MailMerge.Processor: Add field parser object, use in `contains_field`
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/mail-merge-plugin: 1/10] MailMerge.Processor: Add field parser object, use in `contains_field`
- Date: Fri, 14 Aug 2020 06:07:22 +0000 (UTC)
commit 27dbd523175bf94822471d54e7ab91ff5e04bd97
Author: Michael Gratton <mike vee net>
Date: Thu Aug 6 13:21:26 2020 +1000
MailMerge.Processor: Add field parser object, use in `contains_field`
Add a reusable private parser struct, reimplement `contains_field` using
it. Add unit tests for that and `is_mail_merge_template`.
.../plugin/mail-merge/mail-merge-processor.vala | 106 +++++++++++---
.../mail-merge/mail-merge-test-processor.vala | 153 +++++++++++++++++++++
src/client/plugin/mail-merge/mail-merge-test.vala | 1 +
src/client/plugin/mail-merge/mail-merge.vala | 40 ++----
src/client/plugin/mail-merge/meson.build | 1 +
5 files changed, 255 insertions(+), 46 deletions(-)
---
diff --git a/src/client/plugin/mail-merge/mail-merge-processor.vala
b/src/client/plugin/mail-merge/mail-merge-processor.vala
index da0295f97..90c4fe010 100644
--- a/src/client/plugin/mail-merge/mail-merge-processor.vala
+++ b/src/client/plugin/mail-merge/mail-merge-processor.vala
@@ -19,6 +19,81 @@ public class MailMerge.Processor : GLib.Object {
private const string FIELD_END = "}}";
+ private struct Parser {
+
+ public unowned string text;
+ public int index;
+ public bool spent;
+ public bool at_field_start;
+ public bool at_field_end;
+
+ public Parser(string text) {
+ this.text = text;
+ this.index = 0;
+ this.spent = (text.length == 0);
+ this.at_field_start = text.has_prefix(FIELD_START);
+ this.at_field_end = false;
+ }
+
+ public string read_text() {
+ this.at_field_end = false;
+
+ int start = this.index;
+ char c = this.text[this.index];
+ while (c != 0) {
+ this.index++;
+ if (c == FIELD_START[0] &&
+ this.text[this.index] == FIELD_START[1]) {
+ this.index--;
+ this.at_field_start = true;
+ break;
+ }
+ c = this.text[this.index];
+ }
+ if (c == 0) {
+ this.spent = true;
+ }
+ return this.text.slice(start, this.index);
+ }
+
+ public string read_field() {
+ this.at_field_start = false;
+
+ // Skip the opening field separator
+ this.index += FIELD_START.length;
+
+ int start = this.index;
+ char c = this.text[this.index];
+ while (c != 0) {
+ this.index++;
+ if (c == FIELD_END[0]) {
+ if (this.text[this.index] == FIELD_END[1]) {
+ this.index++;
+ this.at_field_end = true;
+ break;
+ }
+ }
+ c = this.text[this.index];
+ }
+ var end = this.index;
+ if (this.at_field_end) {
+ // Don't include the closing field separator
+ end -= FIELD_END.length;
+ } else {
+ // No closing field separator found, so not a valid
+ // field. Move start back so it includes the opening
+ // field separator
+ start -= FIELD_START.length;
+ }
+ if (c == 0 || this.index == this.text.length) {
+ this.spent = true;
+ }
+ return this.text.slice(start, end);
+ }
+
+ }
+
+
public static string to_field(string name) {
return FIELD_START + name + FIELD_END;
}
@@ -51,30 +126,21 @@ public class MailMerge.Processor : GLib.Object {
return found;
}
- private static bool contains_field(string value) {
+ public static bool contains_field(string text) {
+ var parser = Parser(text);
var found = false;
- var index = 0;
- while (!found) {
- var field_start = value.index_of(FIELD_START, index);
- if (field_start < 0) {
- break;
+ while (!parser.spent) {
+ if (parser.at_field_start) {
+ parser.read_field();
+ if (parser.at_field_end) {
+ found = true;
+ break;
+ }
+ } else {
+ parser.read_text();
}
- found = parse_field((string) value.data[field_start:-1]) != null;
- index = field_start + 1;
}
return found;
}
- private static string? parse_field(string value) {
- string? field = null;
- if (value.has_prefix(FIELD_START)) {
- int start = FIELD_START.length;
- int end = value.index_of(FIELD_END, start);
- if (end >= 0) {
- field = value.substring(start, end - FIELD_END.length).strip();
- }
- }
- return field;
- }
-
}
diff --git a/src/client/plugin/mail-merge/mail-merge-test-processor.vala
b/src/client/plugin/mail-merge/mail-merge-test-processor.vala
new file mode 100644
index 000000000..d1c34f93e
--- /dev/null
+++ b/src/client/plugin/mail-merge/mail-merge-test-processor.vala
@@ -0,0 +1,153 @@
+/*
+ * Copyright © 2020 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class MailMerge.TestProcessor : ValaUnit.TestCase {
+
+
+ private class MockEmailIdentifier : Geary.EmailIdentifier {
+
+
+ private int id;
+
+
+ public MockEmailIdentifier(int id) {
+ this.id = id;
+ }
+
+ public override uint hash() {
+ return GLib.int_hash(this.id);
+ }
+
+ public override bool equal_to(Geary.EmailIdentifier other) {
+ return (
+ this.get_type() == other.get_type() &&
+ this.id == ((MockEmailIdentifier) other).id
+ );
+ }
+
+
+ public override string to_string() {
+ return "%s(%d)".printf(
+ this.get_type().name(),
+ this.id
+ );
+ }
+
+ public override GLib.Variant to_variant() {
+ return new GLib.Variant.int32(id);
+ }
+
+ public override int natural_sort_comparator(Geary.EmailIdentifier other) {
+ MockEmailIdentifier? other_mock = other as MockEmailIdentifier;
+ return (other_mock == null) ? 1 : this.id - other_mock.id;
+ }
+
+ }
+
+
+ public TestProcessor() {
+ base("MailMerge.TestProcessor");
+ add_test("contains_field", contains_field);
+ add_test("is_mail_merge_template", is_mail_merge_template);
+ }
+
+ public void contains_field() throws GLib.Error {
+ assert_true(Processor.contains_field("{{test}}"), "{{test}}");
+ assert_true(Processor.contains_field("test {{test}}"), "test {{test}}");
+ assert_true(Processor.contains_field("test {{test}} test"), "test {{test}} test");
+ assert_true(Processor.contains_field("test {{test}}"), "test {{test}}");
+
+ assert_false(Processor.contains_field("{{test"), "{{test");
+ assert_false(Processor.contains_field("{{test}"), "{{test}");
+ assert_false(Processor.contains_field("{test}"), "{test}");
+ assert_false(Processor.contains_field("test}}"), "test}}");
+ assert_false(Processor.contains_field("test {test"), "test {test");
+ assert_false(Processor.contains_field("test {"), "test {");
+ assert_false(Processor.contains_field("test {{"), "test {{");
+ }
+
+ public void is_mail_merge_template() throws GLib.Error {
+ assert_false(
+ Processor.is_mail_merge_template(string_to_email(EMPTY_MESSAGE)),
+ "empty message"
+ );
+ assert_true(
+ Processor.is_mail_merge_template(string_to_email(TEMPLATE_SUBJECT)),
+ "subject"
+ );
+ assert_true(
+ Processor.is_mail_merge_template(string_to_email(TEMPLATE_BODY)),
+ "body"
+ );
+ }
+
+ private Geary.Email string_to_email(string message_text)
+ throws GLib.Error {
+ var message = new Geary.RFC822.Message.from_buffer(
+ new Geary.Memory.StringBuffer(message_text.replace("\n","\r\n"))
+ );
+ return new Geary.Email.from_message(
+ new MockEmailIdentifier(0), message
+ );
+ }
+
+ private const string EMPTY_MESSAGE = """From: Alice <alice example net>
+Sender: Bob <bob example net>
+To: Charlie <charlie example net>
+CC: Dave <dave example net>
+BCC: Eve <eve example net>
+Reply-To: "Alice: Personal Account" <alice example org>
+Subject: Re: Basic text/plain message
+Date: Fri, 21 Nov 1997 10:01:10 -0600
+Message-ID: <3456 example net>
+In-Reply-To: <1234@local.machine.example>
+References: <1234@local.machine.example>
+X-Mailer: Geary Test Suite 1.0
+
+This is the first line.
+
+This is the second line.
+
+""";
+
+ private const string TEMPLATE_SUBJECT = """From: Alice <alice example net>
+Sender: Bob <bob example net>
+To: Charlie <charlie example net>
+CC: Dave <dave example net>
+BCC: Eve <eve example net>
+Reply-To: "Alice: Personal Account" <alice example org>
+Subject: {{hello}}
+Date: Fri, 21 Nov 1997 10:01:10 -0600
+Message-ID: <3456 example net>
+In-Reply-To: <1234@local.machine.example>
+References: <1234@local.machine.example>
+X-Mailer: Geary Test Suite 1.0
+
+This is the first line.
+
+This is the second line.
+
+""";
+
+ private const string TEMPLATE_BODY = """From: Alice <alice example net>
+Sender: Bob <bob example net>
+To: Charlie <charlie example net>
+CC: Dave <dave example net>
+BCC: Eve <eve example net>
+Reply-To: "Alice: Personal Account" <alice example org>
+Subject: Re: Basic text/plain message
+Date: Fri, 21 Nov 1997 10:01:10 -0600
+Message-ID: <3456 example net>
+In-Reply-To: <1234@local.machine.example>
+References: <1234@local.machine.example>
+X-Mailer: Geary Test Suite 1.0
+
+Hello {{name}}!
+
+""";
+
+}
diff --git a/src/client/plugin/mail-merge/mail-merge-test.vala
b/src/client/plugin/mail-merge/mail-merge-test.vala
index 22fab9ee0..39cc6d10d 100644
--- a/src/client/plugin/mail-merge/mail-merge-test.vala
+++ b/src/client/plugin/mail-merge/mail-merge-test.vala
@@ -18,6 +18,7 @@ int main(string[] args) {
GLib.TestSuite root = GLib.TestSuite.get_root();
root.add_suite(new MailMerge.TestReader().suite);
+ root.add_suite(new MailMerge.TestProcessor().suite);
GLib.MainLoop loop = new GLib.MainLoop();
int ret = -1;
diff --git a/src/client/plugin/mail-merge/mail-merge.vala b/src/client/plugin/mail-merge/mail-merge.vala
index de9317a85..798bbed1e 100644
--- a/src/client/plugin/mail-merge/mail-merge.vala
+++ b/src/client/plugin/mail-merge/mail-merge.vala
@@ -21,10 +21,6 @@ public class Plugin.MailMerge :
PluginBase, FolderExtension, EmailExtension, TrustedExtension {
- private const string FIELD_START = "{{";
- private const string FIELD_END = "}}";
-
-
// Translators: Templates folder name alternatives. Separate names
// using a vertical bar and put the most common localized name to
// the front for the default. English names do not need to be
@@ -125,21 +121,6 @@ public class Plugin.MailMerge :
this.folder_names.clear();
}
- private async bool is_mail_merge_template(Email plugin) {
- bool is_merge = false;
- try {
- Geary.Email? email = yield load_merge_email(plugin);
- if (email != null) {
- is_merge = global::MailMerge.Processor.is_mail_merge_template(
- email
- );
- }
- } catch (GLib.Error err) {
- warning("Unable to load merge template: %s", err.message);
- }
- return is_merge;
- }
-
private async void edit_email(EmailIdentifier id) {
try {
var composer = yield this.plugin_application.compose_with_context(
@@ -213,13 +194,20 @@ public class Plugin.MailMerge :
} catch (GLib.Error err) {
warning("Could not load folders for email: %s", err.message);
}
- if (containing.any_match((f) => f.display_name in this.folder_names) &&
- yield is_mail_merge_template(target)) {
- this.email.add_email_info_bar(
- target.identifier,
- new_template_email_info_bar(target.identifier),
- INFO_BAR_PRIORITY
- );
+ if (containing.any_match((f) => f.display_name in this.folder_names)) {
+ try {
+ var email = yield load_merge_email(target);
+ if (global::MailMerge.Processor.is_mail_merge_template(email)) {
+ this.email.add_email_info_bar(
+ target.identifier,
+ new_template_email_info_bar(target.identifier),
+ INFO_BAR_PRIORITY
+ );
+ }
+ } catch (GLib.Error err) {
+ warning("Error checking email for merge templates: %s",
+ err.message);
+ }
}
}
diff --git a/src/client/plugin/mail-merge/meson.build b/src/client/plugin/mail-merge/meson.build
index dcdc67e7f..fd3e9f0ec 100644
--- a/src/client/plugin/mail-merge/meson.build
+++ b/src/client/plugin/mail-merge/meson.build
@@ -14,6 +14,7 @@ lib_src = files(
)
test_src = files(
+ 'mail-merge-test-processor.vala',
'mail-merge-test-reader.vala',
'mail-merge-test.vala'
)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]