home · contact · privacy
Streamline querying. master
authorPlom Heller <plom@plomlompom.com>
Sat, 30 May 2026 03:48:14 +0000 (05:48 +0200)
committerPlom Heller <plom@plomlompom.com>
Sat, 30 May 2026 03:48:14 +0000 (05:48 +0200)
bricksplom.py

index d826f776b18df0b9406b827d217c0587bba83253..8aed7e6aaeeb097de736833567bc3048e319f578 100755 (executable)
@@ -25,7 +25,9 @@ CHAR_COLL_SEP_COLUMN = '-'
 CHAR_COLL_SEP_PAGE = '='
 CHAR_COL_SOLID = '+'
 CHAR_COL_TRANSPARENT = '-'
 CHAR_COLL_SEP_PAGE = '='
 CHAR_COL_SOLID = '+'
 CHAR_COL_TRANSPARENT = '-'
-CHAR_INQUIRY = '?'
+CHAR_Q_EQ_ID = ':'
+CHAR_Q_STUDS = 's'
+CHAR_Q_TEXT = '?'
 BOX_PREFIX = 'box:'
 
 PieceListing = tuple[int, str, str]
 BOX_PREFIX = 'box:'
 
 PieceListing = tuple[int, str, str]
@@ -101,19 +103,18 @@ class Lookupable:
     id_: str
 
     @classmethod
     id_: str
 
     @classmethod
-    def indent_id(cls, id_: str) -> str:
+    def indent_id(
+            cls,
+            id_: str
+            ) -> str:
         'id_ indented by value of cls._id_indent.'
         return (max(0, cls._id_indent - len(id_)) * ' ') + id_
 
         'id_ indented by value of cls._id_indent.'
         return (max(0, cls._id_indent - len(id_)) * ' ') + id_
 
-    def id_indented(self) -> str:
-        'self.id_ indented by value of self._id_indent.'
-        return self.indent_id(self.id_)
-
-    def searchable(
+    def id_indented(
             self
             ) -> str:
             self
             ) -> str:
-        'String analyzed by "?" full-text searches.'
-        return str(self)
+        'self.id_ indented by value of self._id_indent.'
+        return self.indent_id(self.id_)
 
     def show(
             self
 
     def show(
             self
@@ -121,6 +122,24 @@ class Lookupable:
         'Default single-item display of self.'
         return str(self)
 
         'Default single-item display of self.'
         return str(self)
 
+    @property
+    def matchers(
+            self
+            ) -> dict[str, Callable[[str], bool]]:
+        'Available lookup matchers.'
+        return {
+            CHAR_Q_TEXT: lambda q_body: q_body.upper() in str(self).upper()
+            }
+
+    def match(
+            self,
+            query_char: str,
+            query_body: str
+            ) -> bool:
+        'Return if self matches query.'
+        assert query_char in self.matchers
+        return self.matchers[query_char](query_body)
+
 
 class Color(Textfiled, Lookupable):
     'Color incl. solidness/transparency field.'
 
 class Color(Textfiled, Lookupable):
     'Color incl. solidness/transparency field.'
@@ -197,6 +216,15 @@ class Design(Textfiled, Lookupable):
         'Own .id_ plus (sorted) .alternate_ids.'
         return tuple([self.id_] + sorted(list(self.alternate_ids)))
 
         'Own .id_ plus (sorted) .alternate_ids.'
         return tuple([self.id_] + sorted(list(self.alternate_ids)))
 
+    @property
+    def matchers(
+            self
+            ) -> dict[str, Callable[[str], bool]]:
+        def q_studs(q_body) -> bool:
+            assert q_body.isdigit()
+            return self.n_studs == int(q_body)
+        return super().matchers | {CHAR_Q_STUDS: q_studs}
+
     @classmethod
     def from_textfile(
             cls,
     @classmethod
     def from_textfile(
             cls,
@@ -230,7 +258,7 @@ class Design(Textfiled, Lookupable):
             ) -> str:
         return (f'{self.id_indented()} '
                 + (f'={self.alternate_to.id_}' if self.alternate_to
             ) -> str:
         return (f'{self.id_indented()} '
                 + (f'={self.alternate_to.id_}' if self.alternate_to
-                   else ('_'
+                   else (CHAR_DESIGN_DESC
                          + ('?' if self._n_studs < 0 else str(self._n_studs))
                          + f' {self._description}')))
 
                          + ('?' if self._n_studs < 0 else str(self._n_studs))
                          + f' {self._description}')))
 
@@ -368,10 +396,13 @@ class Collection(Textfiled, WithDb, Lookupable):
             ) -> str:
         return f'{self.id_} {self._is_in_str} {self.description}'
 
             ) -> str:
         return f'{self.id_} {self._is_in_str} {self.description}'
 
-    def searchable(
+    @property
+    def matchers(
             self
             self
-            ) -> str:
-        return self.show()
+            ) -> dict[str, Callable[[str], bool]]:
+        return super().matchers | {
+            CHAR_Q_TEXT: lambda q_body: q_body.upper() in self.show().upper()
+            }
 
     def _format_paginated(
             self,
 
     def _format_paginated(
             self,
@@ -568,22 +599,14 @@ class BricksDb:
         assert table_name in self.lookupable
         maybe_dict = getattr(self, table_name)
         table = maybe_dict if isinstance(maybe_dict, dict) else maybe_dict()
         assert table_name in self.lookupable
         maybe_dict = getattr(self, table_name)
         table = maybe_dict if isinstance(maybe_dict, dict) else maybe_dict()
-        if inquiry.startswith(CHAR_INQUIRY):
-            return CHAR_NEWLINE.join(
-                    [(v.raw() if show_raw else str(v)) for
-                     v in sorted(table.values(),
-                                 key=lambda v: f'{v.id_indented()}')
-                     if inquiry[1:].upper() in str(v.searchable()).upper()])
-        if table_name == 'designs' and inquiry.startswith('s'):
-            n_studs_str = inquiry[1:]
-            assert n_studs_str.isdigit()
-            return CHAR_NEWLINE.join(
-                    [(v.raw() if show_raw else str(v)) for
-                     v in sorted(table.values(),
-                                 key=lambda v: f'{v.id_indented()}')
-                     if v.n_studs == int(n_studs_str)])
-        item = table[inquiry]
-        return item.raw() if show_raw else item.show()
+        query_char, query_body = inquiry[0], inquiry[1:]
+        if query_char == CHAR_Q_EQ_ID:
+            item = table[query_body]
+            return item.raw() if show_raw else item.show()
+        return CHAR_NEWLINE.join(
+                [(r.raw() if show_raw else str(r)) for
+                 r in sorted(table.values(), key=lambda r: r.id_indented())
+                 if r.match(query_char, query_body)])
 
 
 def main(
 
 
 def main(
@@ -608,7 +631,7 @@ def main(
     db = BricksDb(environ.get(NAME_ENV_DIRNAME, '.'))
     parser = ArgumentParser()
     add_abbrev_choices_arg(parser, 'table', set(db.lookupable))
     db = BricksDb(environ.get(NAME_ENV_DIRNAME, '.'))
     parser = ArgumentParser()
     add_abbrev_choices_arg(parser, 'table', set(db.lookupable))
-    parser.add_argument('inquiry', nargs='?', default=CHAR_INQUIRY)
+    parser.add_argument('inquiry', nargs='?', default=CHAR_Q_TEXT)
     parser.add_argument('--raw', action='store_true')
     args = parser.parse_args()
     print(db.lookup(args.table, args.inquiry, args.raw).rstrip())
     parser.add_argument('--raw', action='store_true')
     args = parser.parse_args()
     print(db.lookup(args.table, args.inquiry, args.raw).rstrip())