Introducing LunaBuild to the build flow (#36)
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg / api.py
diff --git a/lunaix-os/scripts/build-tools/lcfg/api.py b/lunaix-os/scripts/build-tools/lcfg/api.py
new file mode 100644 (file)
index 0000000..1b0b30f
--- /dev/null
@@ -0,0 +1,139 @@
+from abc import abstractmethod
+
+class ConfigLoadBaseException(Exception):
+    def __init__(self, msg, node, inner=None) -> None:
+        super().__init__(msg)
+        
+        self.__msg = msg
+        self.__node = node
+        self.__inner = inner
+
+    def __str__(self) -> str:
+        return super().__str__()
+    
+class ConfigLoadException(ConfigLoadBaseException):
+    def __init__(self, msg, node = None, inner=None) -> None:
+        super().__init__(msg, node, inner)
+
+class ConfigTypeCheckError(ConfigLoadBaseException):
+    def __init__(self, msg, node, inner=None) -> None:
+        super().__init__(msg, node, inner)
+
+class ConfigIOProvider:
+    def __init__(self) -> None:
+        pass
+
+    @abstractmethod
+    def export(self, env, config_dict):
+        pass
+    
+    @abstractmethod
+    def save(self, env, config_dict):
+        pass
+
+    @abstractmethod
+    def load(self, env):
+        pass
+
+
+class TypeProviderBase:
+    def __init__(self, type) -> None:
+        self._type = type
+
+    @abstractmethod
+    def check(self, val):
+        return True
+    
+    @abstractmethod
+    def serialise(self, val):
+        pass
+
+    @abstractmethod
+    def deserialise(self, val):
+        pass
+
+    @abstractmethod
+    def parse_input(self, input_str):
+        pass
+
+    @abstractmethod
+    def to_input(self, val):
+        pass
+
+    def allow_none(self):
+        return False
+    
+    @staticmethod
+    def typedef_matched(typedef):
+        return False
+    
+    @abstractmethod
+    def __str__(self) -> str:
+        pass
+
+class Renderable:
+    def __init__(self) -> None:
+        pass
+    
+    @abstractmethod
+    def render(self, rctx):
+        pass
+
+class RenderContext:
+    def __init__(self) -> None:
+        pass
+
+    @abstractmethod
+    def add_expandable(self, label, node, on_expand_cb):
+        pass
+
+    @abstractmethod
+    def add_field(self, label, node):
+        pass
+
+class InteractiveRenderer(RenderContext):
+    def __init__(self) -> None:
+        super().__init__()
+
+    @abstractmethod
+    def render_loop(self):
+        pass
+
+
+class LConfigBuiltin:
+    def __init__(self, f, is_contextual, rename=None, caller_type=[]):
+        self.name = f.__name__ if not rename else rename
+        self.__f = f
+        self.__contextual = is_contextual
+        self.__caller_type = caller_type
+
+    def __call__(self, env, *args, **kwds):
+        if not self.__contextual:
+            return self.__f(env, *args, **kwds)
+        
+        caller = env.callframe_at(0)
+        f_name = self.__f.__name__
+
+        if not caller:
+            raise ConfigLoadException(
+                f"contextual function '{f_name}' can not be called contextless",
+                None
+            )
+        
+        if self.__caller_type and not any([isinstance(caller, x) for x in self.__caller_type]):
+            raise ConfigLoadException(
+                f"caller of '{f_name}' ({caller}) must have type of any {self.__caller_type}",
+                caller
+            )
+    
+        return self.__f(env, caller, *args, **kwds)
+    
+def contextual(name=None, caller_type=[]):
+    def wrapper(func):
+        return LConfigBuiltin(func, True, name, caller_type)
+    return wrapper
+
+def builtin(name=None):
+    def wrapper(func):
+        return LConfigBuiltin(func, False, name)
+    return wrapper