From 5e262deda6673bf348465c5bf60fe6ff87304d79 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Fri, 16 Aug 2024 23:15:43 +0100
Subject: [PATCH] Remove config, YAGNI

---
 matchy/files/config.py | 152 -----------------------------------------
 matchy/files/state.py  |   2 +-
 matchy/matching.py     |   9 ++-
 tests/matching_test.py |   3 +-
 tests/state_test.py    |  14 ++--
 5 files changed, 15 insertions(+), 165 deletions(-)
 delete mode 100644 matchy/files/config.py

diff --git a/matchy/files/config.py b/matchy/files/config.py
deleted file mode 100644
index c157bc1..0000000
--- a/matchy/files/config.py
+++ /dev/null
@@ -1,152 +0,0 @@
-"""Very simple config loading library"""
-from schema import Schema, Use, Optional
-import matchy.files.ops as ops
-import os
-import logging
-import json
-
-logger = logging.getLogger("config")
-logger.setLevel(logging.INFO)
-
-# Envar takes precedent
-_ENVAR = "MATCHY_CONFIG"
-_FILE = ".matchy/config.json"
-
-# Warning: Changing any of the below needs proper thought to ensure backwards compatibility
-_VERSION = 2
-
-
-class _Key():
-    VERSION = "version"
-
-    MATCH = "match"
-
-    SCORE_FACTORS = "score_factors"
-    REPEAT_ROLE = "repeat_role"
-    REPEAT_MATCH = "repeat_match"
-    EXTRA_MEMBER = "extra_member"
-    UPPER_THRESHOLD = "upper_threshold"
-
-    # Removed
-    _OWNERS = "owners"
-    _TOKEN = "token"
-
-
-_SCHEMA = Schema(
-    {
-        # The current version
-        _Key.VERSION: Use(int),
-
-        # Settings for the match algorithmn, see matching.py for explanations on usage
-        Optional(_Key.MATCH): {
-            Optional(_Key.SCORE_FACTORS): {
-
-                Optional(_Key.REPEAT_ROLE): Use(int),
-                Optional(_Key.REPEAT_MATCH): Use(int),
-                Optional(_Key.EXTRA_MEMBER): Use(int),
-                Optional(_Key.UPPER_THRESHOLD): Use(int),
-            }
-        }
-    }
-)
-
-_EMPTY_DICT = {
-    _Key.VERSION: _VERSION
-}
-
-
-def _migrate_to_v1(d: dict):
-    # Owners moved to History in v1
-    # Note: owners will be required to be re-added to the state.json
-    if _Key._OWNERS in d:
-        owners = d.pop(_Key._OWNERS)
-        logger.warning(
-            "Migration removed owners from config, these must be re-added to the state.json")
-        logger.warning("Owners: %s", owners)
-
-
-def _migrate_to_v2(d: dict):
-    # Token moved to the environment
-    if _Key._TOKEN in d:
-        del d[_Key._TOKEN]
-
-
-# Set of migration functions to apply
-_MIGRATIONS = [
-    _migrate_to_v1,
-    _migrate_to_v2
-]
-
-
-class _ScoreFactors():
-    def __init__(self, data: dict):
-        """Initialise and validate the config"""
-        self._dict = data
-
-    @property
-    def repeat_role(self) -> int:
-        return self._dict.get(_Key.REPEAT_ROLE, None)
-
-    @property
-    def repeat_match(self) -> int:
-        return self._dict.get(_Key.REPEAT_MATCH, None)
-
-    @property
-    def extra_member(self) -> int:
-        return self._dict.get(_Key.EXTRA_MEMBER, None)
-
-    @property
-    def upper_threshold(self) -> int:
-        return self._dict.get(_Key.UPPER_THRESHOLD, None)
-
-
-class _Config():
-    def __init__(self, data: dict):
-        """Initialise and validate the config"""
-        _SCHEMA.validate(data)
-        self._dict = data
-
-    @property
-    def token(self) -> str:
-        return self._dict["token"]
-
-    @property
-    def score_factors(self) -> _ScoreFactors:
-        return _ScoreFactors(self._dict.get(_Key.SCORE_FACTORS, {}))
-
-
-def _migrate(dict: dict):
-    """Migrate a dict through versions"""
-    version = dict.get("version", 0)
-    for i in range(version, _VERSION):
-        _MIGRATIONS[i](dict)
-        dict["version"] = _VERSION
-
-
-def _load() -> _Config:
-    """
-    Load the state from an envar or file
-    Apply any required migrations
-    """
-
-    # Try the envar first
-    envar = os.environ.get(_ENVAR)
-    if envar:
-        loaded = json.loads(envar)
-        logger.info("Config loaded from $%s", _ENVAR)
-    else:
-        # Otherwise try the file
-        if os.path.isfile(_FILE):
-            loaded = ops.load(_FILE)
-            logger.info("Config loaded from %s", _FILE)
-        else:
-            loaded = _EMPTY_DICT
-            logger.warning("No %s file found, using defaults", _FILE)
-
-    _migrate(loaded)
-    return _Config(loaded)
-
-
-# Core config for users to use
-# Singleton as there should only be one, it's static, and global
-Config = _load()
diff --git a/matchy/files/state.py b/matchy/files/state.py
index 438cf59..33c3122 100644
--- a/matchy/files/state.py
+++ b/matchy/files/state.py
@@ -191,7 +191,7 @@ class State():
         before storing the dict back in the State
         """
         @wraps(func)
-        def inner(self: State, *args, **kwargs):
+        def inner(self, *args, **kwargs):
             tmp = State(self._dict, self._file)
             func(tmp, *args, **kwargs)
             _SCHEMA.validate(tmp._dict)
diff --git a/matchy/matching.py b/matchy/matching.py
index 5d9b9b2..94d84f5 100644
--- a/matchy/matching.py
+++ b/matchy/matching.py
@@ -5,7 +5,6 @@ from datetime import datetime
 from typing import Protocol, runtime_checkable
 from matchy.files.state import State, ts_to_datetime
 import matchy.util as util
-import matchy.files.config as config
 
 
 class _ScoreFactors(int):
@@ -15,14 +14,14 @@ class _ScoreFactors(int):
     """
 
     # Added for each role the matchee has that another group member has
-    REPEAT_ROLE = config.Config.score_factors.repeat_role or 2**2
+    REPEAT_ROLE = 2**2
     # Added for each member in the group that the matchee has already matched with
-    REPEAT_MATCH = config.Config.score_factors.repeat_match or 2**3
+    REPEAT_MATCH = 2**3
     # Added for each additional member over the set "per group" value
-    EXTRA_MEMBER = config.Config.score_factors.extra_member or 2**5
+    EXTRA_MEMBER = 2**5
 
     # Upper threshold, if the user scores higher than this they will not be placed in that group
-    UPPER_THRESHOLD = config.Config.score_factors.upper_threshold or 2**6
+    UPPER_THRESHOLD = 2**6
 
 
 logger = logging.getLogger("matching")
diff --git a/tests/matching_test.py b/tests/matching_test.py
index b99c33f..0a1dc05 100644
--- a/tests/matching_test.py
+++ b/tests/matching_test.py
@@ -403,7 +403,8 @@ def test_auth_scopes():
     tmp_state.set_user_scope(id, state.AuthScope.MATCHER)
     assert tmp_state.get_user_has_scope(id, state.AuthScope.MATCHER)
 
-    tmp_state.validate()
+    # Validate the state by constucting a new one
+    _ = state.State(tmp_state._dict)
 
 
 def test_iterate_all_shifts():
diff --git a/tests/state_test.py b/tests/state_test.py
index de7a185..f34578f 100644
--- a/tests/state_test.py
+++ b/tests/state_test.py
@@ -4,6 +4,7 @@
 import matchy.files.state as state
 import tempfile
 import os
+import matchy.files.ops as ops
 
 
 def test_basic_state():
@@ -18,10 +19,11 @@ def test_simple_load_reload():
     with tempfile.TemporaryDirectory() as tmp:
         path = os.path.join(tmp, 'tmp.json')
         st = state.load_from_file(path)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
+        ops.save(st._file, st._dict)
 
         st = state.load_from_file(path)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
         st = state.load_from_file(path)
 
 
@@ -30,13 +32,13 @@ def test_authscope():
     with tempfile.TemporaryDirectory() as tmp:
         path = os.path.join(tmp, 'tmp.json')
         st = state.load_from_file(path)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
 
         assert not st.get_user_has_scope(1, state.AuthScope.MATCHER)
 
         st = state.load_from_file(path)
         st.set_user_scope(1, state.AuthScope.MATCHER)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
 
         st = state.load_from_file(path)
         assert st.get_user_has_scope(1, state.AuthScope.MATCHER)
@@ -50,13 +52,13 @@ def test_channeljoin():
     with tempfile.TemporaryDirectory() as tmp:
         path = os.path.join(tmp, 'tmp.json')
         st = state.load_from_file(path)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
 
         assert not st.get_user_active_in_channel(1, "2")
 
         st = state.load_from_file(path)
         st.set_user_active_in_channel(1, "2", True)
-        st._save_to_file()
+        ops.save(st._file, st._dict)
 
         st = state.load_from_file(path)
         assert st.get_user_active_in_channel(1, "2")