home · contact · privacy
Split set_and_check_for_change into recursive node-walk end end-node processing.
authorChristian Heller <c.heller@plomlompom.de>
Thu, 11 Sep 2025 14:23:40 +0000 (16:23 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Thu, 11 Sep 2025 14:23:40 +0000 (16:23 +0200)
ircplom/client_tui.py

index 7a5a09274ec2c84026ba4667019d99ebaaafa761..d1f026d33588feb155ee6b659cc77cfb3b62ae1d 100644 (file)
@@ -45,24 +45,28 @@ class _UpdatingNode(AutoAttrMixin):
                 scopes = c.log_scopes | scopes
         return scopes.get(path, scopes[''])
 
-    def set_and_check_for_change(self, update: _Update
-                                 ) -> Optional[tuple[LogScope, Any]]:
+    def recursive_set_and_report_change(
+            self, update: _Update) -> Optional[tuple[LogScope, Any]]:
         'Apply update, return if that actually made a difference.'
-        key = update.path[0]
-        node = self._get(key)
-        scope = self._scope(key)
-        if len(update.path) == 1:
-            if update.value is None:
-                if not self._is_set(key):
-                    return None
-                self._unset(key)
-                return (scope, None)
-            if node == update.value:
+        node = self._get(update.path[0])
+        if len(update.path) > 1:
+            return node.recursive_set_and_report_change(
+                    _Update(update.path[1:], update.value))
+        return self._focused_set_and_report_change(node, update)
+
+    def _focused_set_and_report_change(
+            self, old_value: '_UpdatingNode', update: _Update
+            ) -> Optional[tuple[LogScope, Any]]:
+        scope = self._scope(update.path[0])
+        if update.value is None:
+            if not self._is_set(update.path[0]):
                 return None
-            self._set(key, update.value)
-            return (scope, update.value)
-        return node.set_and_check_for_change(_Update(update.path[1:],
-                                                     update.value))
+            self._unset(update.path[0])
+            return (scope, None)
+        if old_value == update.value:
+            return None
+        self._set(update.path[0], update.value)
+        return (scope, update.value)
 
     def _get(self, key: str):
         return getattr(self, key)
@@ -200,20 +204,21 @@ class _UpdatingChannel(_UpdatingNode):
     topic: Topic = Topic()
     log_scopes = {'': LogScope.CHAT}
 
-    def set_and_check_for_change(self, update: _Update
-                                 ) -> Optional[tuple[LogScope, Any]]:
+    def _focused_set_and_report_change(
+            self, old_value: '_UpdatingNode', update: _Update
+            ) -> Optional[tuple[LogScope, Any]]:
         scope = self._scope(update.path[0])
-        if update.path[-1] == 'topic':
-            if super().set_and_check_for_change(update):
+        if update.path[0] == 'topic':
+            if super()._focused_set_and_report_change(old_value, update):
                 return (scope,
                         f'RAW:{self.topic.who} set topic: {self.topic.what}')
             return None
-        assert update.path[-1] == 'user_ids'
+        assert update.path[0] == 'user_ids'
         assert isinstance(update.value, tuple)
         d = {'NUHS:joining': tuple(id_ for id_ in update.value
                                    if id_ not in self.user_ids)
              } if self.user_ids else {'NICKS:residents': tuple(update.value)}
-        if super().set_and_check_for_change(update):
+        if super()._focused_set_and_report_change(old_value, update):
             return scope, d
         return None
 
@@ -224,17 +229,18 @@ class _UpdatingUser(_UpdatingNode, NickUserHost):
     modes = '?'
     exit_msg = ''
 
-    def set_and_check_for_change(self, update: _Update
-                                 ) -> Optional[tuple[LogScope, Any]]:
+    def _focused_set_and_report_change(
+            self, old_value: '_UpdatingNode', update: _Update
+            ) -> Optional[tuple[LogScope, Any]]:
         assert isinstance(update.value, str)
-        if update.path[-1] == 'modes':
-            return super().set_and_check_for_change(update)
-        if super().set_and_check_for_change(update):
+        if update.path[0] == 'modes':
+            return super()._focused_set_and_report_change(old_value, update)
+        if super()._focused_set_and_report_change(old_value, update):
             scope = self._scope(update.path[0])
             msg = 'RAW: '
-            if update.path[-1] == 'nick':
+            if update.path[0] == 'nick':
                 return scope, msg + f'{self.prev} renames {update.value}'
-            if update.path[-1] == 'exit_msg' and update.value:
+            if update.path[0] == 'exit_msg' and update.value:
                 msg += f'{self}'
                 msg += 'quits' if update.value[0] == 'Q' else 'parts'
                 if len(update.value) > 1:
@@ -339,7 +345,7 @@ class _ClientWindowsManager:
                     if isinstance(update.value, get_original_bases(cls)[1])]:
             update.value = cls(**dc_asdict(update.value))  # type: ignore
             break
-        result = self.db.set_and_check_for_change(update)
+        result = self.db.recursive_set_and_report_change(update)
         if result is None:
             return False
         scope, value = result