summary refs log tree commit diff stats
path: root/ganarchy/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'ganarchy/config.py')
-rw-r--r--ganarchy/config.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/ganarchy/config.py b/ganarchy/config.py
new file mode 100644
index 0000000..154447b
--- /dev/null
+++ b/ganarchy/config.py
@@ -0,0 +1,157 @@
+# This file is part of GAnarchy - decentralized project hub
+# Copyright (C) 2019  Soni L.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import abc
+import os
+
+import abdl
+import qtoml
+
+from enum import Enum
+
+# sanitize = skip invalid entries
+# validate = error on invalid entries
+CONFIG_REPOS_SANITIZE = abdl.compile("""->'projects'?:?$dict
+                                          ->commit/[0-9a-fA-F]{40}|[0-9a-fA-F]{64}/?:?$dict
+                                            ->url:?$dict
+                                              ->branch:?$dict(->'active'?:?$bool)""", {'bool': bool, 'dict': dict})
+CONFIG_REPOS = abdl.compile("->'projects'->commit->url->branch", {'dict': dict})
+
+CONFIG_TITLE_SANITIZE = abdl.compile("""->title'title'?:?$str""", {'str': str})
+CONFIG_BASE_URL_SANITIZE = abdl.compile("""->base_url'base_url'?:?$str""", {'str': str})
+CONFIG_SRCS_SANITIZE = abdl.compile("""->'config_srcs'?:?$list->src:?$str""", {'list': list, 'str': str})
+
+CONFIG_TITLE_VALIDATE = abdl.compile("""->title'title':$str""", {'str': str})
+CONFIG_BASE_URL_VALIDATE = abdl.compile("""->base_url'base_url':$str""", {'str': str})
+CONFIG_SRCS_VALIDATE = abdl.compile("""->'config_srcs':$list->src:$str""", {'list': list, 'str': str})
+
+class ConfigProperty(Enum):
+    TITLE = 1
+    BASE_URL = 2
+
+class ConfigSource(abc.ABC):
+    @abc.abstractmethod
+    def update(self):
+        """Refreshes the config if necessary."""
+        pass
+
+    @abc.abstractmethod
+    def exists(self):
+        """Returns whether the config exists."""
+        pass
+
+    def is_domain_blocked(self, domain):
+        """Returns whether the given domain is blocked."""
+        return False
+
+    def get_remote_config_sources(self):
+        """Yields URI strings for additional configs.
+
+        Yields:
+            str: A remote config URI.
+
+        """
+        yield from ()
+
+    @abc.abstractmethod
+    def get_project_commit_tree_paths(self):
+        """Yields (project, URI, branch, options) tuples.
+
+        Yields:
+            tuple of (str, str, str, dict): A project commit-tree path.
+
+            Composed of a project commit hash, a repo URI, a branch name
+            and a dict of options respectively.
+
+        """
+        pass
+
+    def get_supported_properties(self):
+        """Returns an iterable of properties supported by this config source.
+
+        Returns:
+            Iterable of ConfigProperty: Supported properties.
+
+        """
+        return ()
+
+    def get_property_value(self, prop):
+        """Returns the value associated with the given property.
+
+        Args:
+            prop (ConfigProperty): The property.
+
+        Returns:
+            The value associated with the given property.
+
+        Raises:
+            ValueError: If the property is not supported by this config
+            source.
+
+        """
+        raise ValueError
+
+class FileConfigSource(ConfigSource):
+    SUPPORTED_PROPERTIES = {}
+
+    def __init__(self, filename):
+        self.file_exists = False
+        self.last_updated = None
+        self.filename = filename
+        self.tomlobj = None
+
+    def update(self):
+        try:
+            updtime = self.last_updated
+            self.last_updated = os.stat(self.filename).st_mtime
+            if not self.file_exists or updtime != self.last_updated:
+                with open(self.filename) as f:
+                    self.tomlobj = qtoml.load(f)
+            self.file_exists = True
+        except OSError:
+            return
+
+    def exists(self):
+        return self.file_exists
+
+    def get_remote_config_sources(self):
+        for r in CONFIG_SRCS_SANITIZE.match(self.tomlobj):
+            yield r['src'][1]
+
+    def get_project_commit_tree_paths(self):
+        for r in CONFIG_PATTERN_SANITIZE.match(self.tomlobj):
+            yield (r['commit'][0], r['url'][0], r['branch'][0], r['branch'][1])
+
+    @classmethod
+    def get_supported_properties(cls):
+        return cls.SUPPORTED_PROPERTIES
+
+class RemoteConfigSource(ConfigSource):
+    def __init__(self, uri):
+        self.uri = uri
+        self.tomlobj = None
+        self.remote_exists = False
+
+    def update(self):
+        raise NotImplementedError
+
+    def exists(self):
+        return self.remote_exists
+
+    def get_project_commit_tree_paths(self):
+        for r in CONFIG_PATTERN_SANITIZE.match(self.tomlobj):
+            yield (r['commit'][0], r['url'][0], r['branch'][0], r['branch'][1])
+