]> git.walde.dev - beanbeanbean/commitdiff
Clean recurring code
authorDustin Walde <redacted>
Sun, 12 Nov 2023 21:09:17 +0000 (13:09 -0800)
committerDustin Walde <redacted>
Sun, 12 Nov 2023 21:09:17 +0000 (13:09 -0800)
- Better date format token config + validation
- Reuse shared code

src/beanbeanbean/recurring.py

index 023f9993b0cf2abc910dac24fd04328fadec138a..22061127e0ce0f78e1124b4287a393c056b82e5b 100644 (file)
@@ -3,6 +3,7 @@ from decimal import (
     Decimal,
     localcontext,
 )
+import re
 
 from beancount.core import amount, position
 from beancount.core.convert import get_weight
@@ -19,12 +20,13 @@ import recurrent
 
 from beanbeanbean.balance import balance
 
-from .utils import flag
+from .utils import flag, make_error
 
 from typing import List, Optional, Union
 
-RECURRING_KEYS = ['recur', 'recurring', 'repeat', 'repeating']
 AMORTIZE_KEYS = ['amortize']
+DEFAULT_DATEFMT_TOKEN = "/"
+RECURRING_KEYS = ['recur', 'recurring', 'repeat', 'repeating']
 
 __plugins__ = ('recurring',)
 
@@ -90,10 +92,7 @@ def handle_recurring_transaction(txn: Transaction, format_token: str) -> Union[L
                 break
 
     if phrase is None:
-        return LoadError(
-            source=new_metadata(txn.meta["filename"], txn.meta["lineno"]),
-            message="Recurring metadata key found, but missing phrase",
-            entry=txn)
+        return make_error(txn, "Recurring metadata key found, but missing phrase")
 
     rr = generate_rrule_from_string(phrase, txn.date)
     dates = [dt for dt in rr]
@@ -194,25 +193,36 @@ def handle_recurring_transaction(txn: Transaction, format_token: str) -> Union[L
     return entries
 
 
-def recurring(entries: Entries, options_map, config_string=""):
-    del options_map, config_string # unused
+def _validate_datefmt_token(config_token: str, errors: list) -> str:
+    if not re.match(r"[a-zA-Z0-9\-_/.]", config_token):
+        errors.append(LoadError(
+            entry=None,
+            message="Invalid dateformat replace string: {}".format(config_token),
+            source=new_metadata(filename="<load>", lineno=0)))
+        return DEFAULT_DATEFMT_TOKEN
+    return config_token
+
+
+def recurring(entries: Entries, options_map, config_string=DEFAULT_DATEFMT_TOKEN):
+    del options_map # unused
     out_entries = []
     errors = []
+
+    dateformat_token = _validate_datefmt_token(config_string, errors)
+
     for entry in entries:
         if not is_recurring_transaction(entry):
             out_entries.append(entry)
         else:
             try:
-                res = handle_recurring_transaction(entry, "/")
+                res = handle_recurring_transaction(entry, dateformat_token)
                 if type(res) is list:
                     out_entries.extend(res)
                 else:
                     errors.append(res)
             except Exception as e:
-                errors.append(LoadError(
-                    source=new_metadata(entry.meta["filename"], entry.meta["lineno"]),
-                    message="Failed to handle recurring transaction: {}".format(e),
-                    entry=entry))
+                errors.append(make_error(entry,
+                    "Failed to handle recurring transaction: {}".format(e)))
                 out_entries.append(flag(entry))
 
     return out_entries, errors