From 25308812f0dab570fe05fea9d704673d562d53e2 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Tue, 30 Mar 2021 01:25:35 +0300 Subject: cgitize.git: proper config writer classes --- cgitize/git.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/cgitize/git.py b/cgitize/git.py index a2be8df..78679d7 100644 --- a/cgitize/git.py +++ b/cgitize/git.py @@ -39,6 +39,102 @@ class Config: finally: self.write(old_contents) + # What follows is an exteremely loose interpretation of what the .gitconfig + # syntax is. The source was git-config(1). + + class Section: + def __init__(self, name, variables): + Config.Section.validate_name(name) + self.name = name + self.variables = variables + + @staticmethod + def validate_name(name): + if not name: + raise RuntimeError('section names cannot be empty') + for c in name: + if c.isalnum() or c == '-' or c == '.': + continue + raise RuntimeError(f'section names must only contain alphanumeric characters, . or -: {name}') + + @staticmethod + def format_name(name): + return name + + def format(self): + result = '[self.format_name(self.name)]\n' + result += ''.join((var.format() for var in self.variables)) + return result + + class Subsection: + def __init__(self, section, name, variables): + Config.Section.validate_name(section) + Config.Subsection.validate_name(name) + self.section = section + self.name = name + self.variables = variables + + @staticmethod + def validate_name(name): + if '\n' in name: + raise RuntimeError(f'subsection names cannot contain newlines: {name}') + + def format_name(self): + name = self.name + # Escape the backslashes: + name = name.replace('\\', '\\\\') + # Escape the quotes: + name = name.replace('"', '\\"') + # Put in quotes: + return f'"{name}"' + + def format(self): + result = f'[{Config.Section.format_name(self.section)} {self.format_name()}]\n' + result += ''.join((var.format() for var in self.variables)) + return result + + class Variable: + def __init__(self, name, value): + Config.Variable.validate_name(name) + Config.Variable.validate_value(value) + self.name = name + self.value = value + + @staticmethod + def validate_name(name): + if not name: + raise RuntimeError('variable names cannot be empty') + for c in name: + if c.isalnum() or c == '-': + continue + raise RuntimeError(f'variable name can only contain alphanumeric characters or -: {name}') + if not name[0].isalnum(): + raise RuntimeError(f'variable name must start with an alphanumeric character: {name}') + + @staticmethod + def validate_value(value): + pass + + def format_name(self): + return self.name + + def format_value(self): + value = self.value + # Escape the backslashes: + value = value.replace('\\', '\\\\') + # Escape the supported escape sequences (\n, \t and \b): + value = value.replace('\n', '\\n') + value = value.replace('\t', '\\t') + value = value.replace('\b', '\\b') + # Escape the quotes: + value = value.replace('"', '\\"') + # Put in quotes: + value = f'"{value}"' + return value + + def format(self): + return f' {self.format_name()} = {self.format_value()}\n' + class Git: EXE = 'git' @@ -64,9 +160,8 @@ class Git: config = Git.get_global_config() with utils.protected_file(config.path): with config.backup() as old_contents: - new_contents = f'''{old_contents} -[url "{repo.clone_url_with_auth}"] - insteadOf = {repo.clone_url} -''' + variables = [Config.Variable('insteadOf', repo.clone_url)] + subsection = Config.Subsection('url', repo.clone_url_with_auth, variables) + new_contents = f'{old_contents}\n{subsection.format()}' config.write(new_contents) yield -- cgit v1.2.3