import os from . import MinetestCheckError, ContentType from .config import parse_conf def get_base_dir(path): if not os.path.isdir(path): raise IOError("Expected dir") root, subdirs, files = next(os.walk(path)) if len(subdirs) == 1 and len(files) == 0: return get_base_dir(path + "/" + subdirs[0]) else: return path def detect_type(path): if os.path.isfile(path + "/game.conf"): return ContentType.GAME elif os.path.isfile(path + "/init.lua"): return ContentType.MOD elif os.path.isfile(path + "/modpack.txt") or \ os.path.isfile(path + "/modpack.conf"): return ContentType.MODPACK elif os.path.isdir(path + "/mods"): return ContentType.GAME elif os.path.isfile(path + "/texture_pack.conf"): return ContentType.TXP else: return ContentType.UNKNOWN class PackageTreeNode: def __init__(self, baseDir, relative, author=None, repo=None, name=None): print(baseDir) self.baseDir = baseDir self.relative = relative self.author = author self.name = name self.repo = repo self.meta = None self.children = [] # Detect type self.type = detect_type(baseDir) self.read_meta() if self.type == ContentType.GAME: if not os.path.isdir(baseDir + "/mods"): raise MinetestCheckError(("game at {} does not have a mods/ folder").format(self.relative)) self.add_children_from_mod_dir(baseDir + "/mods") elif self.type == ContentType.MODPACK: self.add_children_from_mod_dir(baseDir) def read_meta(self): result = {} # .conf file try: with open(self.baseDir + "/mod.conf", "r") as myfile: conf = parse_conf(myfile.read()) for key in ["name", "description", "title", "depends", "optional_depends"]: try: result[key] = conf[key] except KeyError: pass except IOError: pass # description.txt if not "description" in result: try: with open(self.baseDir + "/description.txt", "r") as myfile: result["description"] = myfile.read() except IOError: pass # depends.txt import re pattern = re.compile("^([a-z0-9_]+)\??$") if not "depends" in result and not "optional_depends" in result: try: with open(self.baseDir + "/depends.txt", "r") as myfile: contents = myfile.read() soft = [] hard = [] for line in contents.split("\n"): line = line.strip() if pattern.match(line): if line[len(line) - 1] == "?": soft.append( line[:-1]) else: hard.append(line) result["depends"] = hard result["optional_depends"] = soft except IOError: pass else: if "depends" in result: result["depends"] = [x.strip() for x in result["depends"].split(",")] if "optional_depends" in result: result["optional_depends"] = [x.strip() for x in result["optional_depends"].split(",")] # Calculate Title if "name" in result and not "title" in result: result["title"] = result["name"].replace("_", " ").title() # Calculate short description if "description" in result: desc = result["description"] idx = desc.find(".") + 1 cutIdx = min(len(desc), 200 if idx < 5 else idx) result["short_description"] = desc[:cutIdx] if "name" in result: self.name = result["name"] del result["name"] self.meta = result def add_children_from_mod_dir(self, dir): for entry in next(os.walk(dir))[1]: path = os.path.join(dir, entry) if not entry.startswith('.') and os.path.isdir(path): child = PackageTreeNode(path, self.relative + entry + "/", name=entry) if not child.type.isModLike(): raise MinetestCheckError(("Expecting mod or modpack, found {} at {} inside {}") \ .format(child.type.value, child.relative, self.type.value)) self.children.append(child) def fold(self, attr, key=None, acc=None): if acc is None: acc = set() if self.meta is None: return acc at = getattr(self, attr) value = at if key is None else at.get(key) if isinstance(value, list): acc |= set(value) elif value is not None: acc.add(value) for child in self.children: child.fold(attr, key, acc) return acc def get(self, key): return self.meta.get(key) def validate(self): for child in self.children: child.validate()