1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Copyright (c) 2024 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import logging 19from pathlib import Path 20from typing import Any, Iterable, Protocol 21 22import rich.syntax 23 24from arkdb.logs import RichLogger 25 26 27def syntax(code: str, **kwargs) -> rich.syntax.Syntax: 28 kwargs = { 29 "line_numbers": True, 30 "start_line": 0, 31 **kwargs, 32 } 33 return rich.syntax.Syntax(code, lexer="ts", **kwargs) 34 35 36class RunnableModule(Protocol): 37 # Read-only fields 38 @property 39 def entry_abc(self) -> Path: 40 pass 41 42 @property 43 def boot_abc(self) -> list[Path]: 44 pass 45 46 def check_exists(self): 47 pass 48 49 50class ScriptFile(RunnableModule): 51 def __init__(self, source_file: Path, panda_file: Path, ast: dict[str, Any] | None = None) -> None: 52 if not source_file.exists(): 53 raise FileNotFoundError(source_file) 54 self.source_file = source_file 55 self.panda_file = panda_file 56 self._ast: dict[str, Any] | None = ast 57 self._disasm_file: Path | None = None 58 59 @property 60 def ast(self) -> dict[str, Any]: 61 if self._ast is None: 62 raise RuntimeError() 63 return self._ast 64 65 @ast.setter 66 def ast(self, new_ast: dict[str, Any]): 67 self._ast = new_ast 68 69 @property 70 def disasm_file(self) -> Path: 71 if not (self._disasm_file and self._disasm_file.exists()): 72 raise FileNotFoundError(self._disasm_file) 73 return self._disasm_file 74 75 @disasm_file.setter 76 def disasm_file(self, file: Path): 77 self._disasm_file = file 78 79 @property 80 def entry_abc(self): 81 return self.panda_file 82 83 @property 84 def boot_abc(self): 85 return [] 86 87 def read_text(self) -> str: 88 if not self.source_file.exists(): 89 raise FileNotFoundError(self.source_file) 90 return self.source_file.read_text() 91 92 def check_exists(self): 93 if not self.source_file.exists(): 94 raise FileNotFoundError(self.source_file) 95 if not self.panda_file.exists(): 96 raise FileNotFoundError(self.panda_file) 97 98 def syntax(self, **kwargs) -> rich.syntax.Syntax: 99 return syntax(self.read_text(), **kwargs) 100 101 def log(self, log: RichLogger, level: int = logging.INFO, **kwargs) -> None: 102 log.log( 103 level, 104 "%s", 105 self.source_file, 106 rich=self.syntax(), 107 stacklevel=2, 108 ) 109 110 111class ArkTsModule(RunnableModule): 112 def __init__(self, entry_file: ScriptFile, boot_files: Iterable[ScriptFile] | None = None): 113 self.entry_file = entry_file 114 self.boot_files = list(boot_files) if boot_files else [] 115 116 @property 117 def entry_abc(self): 118 return self.entry_file.panda_file 119 120 @property 121 def boot_abc(self): 122 return [f.panda_file for f in self.boot_files] 123 124 def check_exists(self): 125 self.entry_file.check_exists() 126 for f in self.boot_files: 127 f.check_exists() 128