JFIF$        dd7 

Viewing File: /usr/libexec/dovecot/settings-history.py

#!/usr/bin/python3

"""Generate history structs for renamed settings and changed default values."""

import argparse
import sys
from pathlib import Path

FILE_TEMPLATE = """\
static const struct setting_history_rename settings_history_core_renames[] = {
%s\
};
static const struct setting_history_default settings_history_core_defaults[] = {
%s\
};
"""

STRUCT_TEMPLATE = """\
  { "%s", "%s", "%s" },
"""


class SettingDefault:
    """Handle the logic behind a setting's changed default value."""

    def __init__(self, key: str, old_value: str, version_text: str, version: [int]):
        """Initialize a setting's default value change object."""
        self.key = key
        self.old_value = old_value
        self.version_text = version_text
        self.version = version

    def render(self) -> str:
        """Render this setting's default value update to text."""
        return STRUCT_TEMPLATE % (self.key, self.old_value, self.version_text)


class SettingRename:
    """Handle the logic behind a setting's changed name."""

    def __init__(self, old_key: str, new_key: str, version_text: str, version: [int]):
        """Initialize a setting's default value change object."""
        self.old_key = old_key
        self.new_key = new_key
        self.version_text = version_text
        self.version = version

    def render(self) -> str:
        """Render this setting's rename to text."""
        return STRUCT_TEMPLATE % (self.old_key, self.new_key, self.version_text)


def die(message: str):
    """Die with a message."""
    module_filename = Path(__file__).name
    print(f"{module_filename}: {message}", file=sys.stderr)
    sys.exit(1)


def parse_version(version: str) -> [int]:
    """Parse a string version into a list of integers."""
    values = version.split(".")
    parsed = []
    for value in values:
        try:
            parsed.append(int(value))
        except ValueError as e:
            raise ValueError("Invalid version {version}: {e}") from e
    return parsed


def render_version(version: [int]) -> str:
    """Produce a textual render of the a version."""
    return ".".join([str(v) for v in version])


def process_version(ce_version: str, pro_version: str, pro: bool) -> (str, [int]):
    """Parse and validate version information."""
    version_text = pro_version if pro else ce_version
    version = parse_version(version_text) if version_text not in ("", "-") else None
    return (version_text, version)


def check_version(prev_version: [int], cur_version: [int]):
    """Fail if version ordering is incorrect."""
    if prev_version is not None and cur_version > prev_version:
        cur_version_text = render_version(cur_version)
        prev_version_text = render_version(prev_version)
        raise ValueError(
            "Invalid version sort order "
            f"between {cur_version_text} and {prev_version_text}: "
            "Please fix the input file"
        )
    return cur_version


def process(input_file: str, contents: str, pro: bool) -> (str, str):
    """Produce the renames and defaults structs from the input data."""
    renames = ""
    defaults = ""
    renames_prev_version = None
    defaults_prev_version = None
    for line, data in enumerate(contents.splitlines()):
        line = line + 1
        values = data.split("\t")

        if len(values) != 5:
            die(
                f"{input_file}:{line}: "
                f"Invalid contents `{data}`: "
                "Expecting 5 fields"
            )

        try:
            (version_text, version) = process_version(
                ce_version=values[3], pro_version=values[4], pro=pro
            )
        except ValueError as e:
            die(f"{input_file}:{line}: {e}")

        if version is None:
            continue

        if values[0] == "rename":
            try:
                renames_prev_version = check_version(renames_prev_version, version)
            except ValueError as e:
                die(f"{input_file}:{line}: {e}")

            rename = SettingRename(
                old_key=values[1],
                new_key=values[2],
                version_text=version_text,
                version=version,
            )
            renames += rename.render()
        elif values[0] == "default":
            try:
                defaults_prev_version = check_version(defaults_prev_version, version)
            except ValueError as e:
                die(f"{input_file}:{line}: {e}")

            default = SettingDefault(
                key=values[1],
                old_value=values[2],
                version_text=version_text,
                version=version,
            )
            defaults += default.render()
        else:
            die(f"{input_file}:{line}: Unrecognized marker in `{data}`")
    return (renames, defaults)


def main():
    """Entry point."""
    parser = argparse.ArgumentParser(
        prog="settings-history.py",
        description="Generate header file for settings migration data",
    )
    parser.add_argument(
        "input-file",
        type=str,
        help="Input data file e.g. settings-history-core.txt",
    )
    parser.add_argument(
        "output-file",
        type=str,
        help="Output header file e.g. settings-history-core.h",
    )
    parser.add_argument(
        "--pro",
        type=int,
        choices=[0, 1],
        help="Whether to generate settings migration data for Pro",
    )
    parser.add_argument(
        "--plugin",
        type=str,
        default="core",
        help="Used to generate settings for the named plugin",
    )
    args = parser.parse_args()

    input_file = getattr(args, "input-file")
    output_file = getattr(args, "output-file")
    plugin_name = args.plugin

    with open(input_file, mode="r", encoding="utf-8") as f_in:
        contents = f_in.read()
        (renames, defaults) = process(input_file, contents, pro=bool(args.pro))

        with open(output_file, mode="w", encoding="utf-8") as f_out:
            template = FILE_TEMPLATE.replace("core", plugin_name)
            f_out.write(template % (renames, defaults))


if __name__ == "__main__":
    main()
Back to Directory  nL+D550H?Mx ,D"v]qv;6*Zqn)ZP0!1 A "#a$2Qr D8 a Ri[f\mIykIw0cuFcRı?lO7к_f˓[C$殷WF<_W ԣsKcëIzyQy/_LKℂ;C",pFA:/]=H  ~,ls/9ć:[=/#f;)x{ٛEQ )~ =𘙲r*2~ a _V=' kumFD}KYYC)({ *g&f`툪ry`=^cJ.I](*`wq1dđ#̩͑0;H]u搂@:~וKL Nsh}OIR*8:2 !lDJVo(3=M(zȰ+i*NAr6KnSl)!JJӁ* %݉?|D}d5:eP0R;{$X'xF@.ÊB {,WJuQɲRI;9QE琯62fT.DUJ;*cP A\ILNj!J۱+O\͔]ޒS߼Jȧc%ANolՎprULZԛerE2=XDXgVQeӓk yP7U*omQIs,K`)6\G3t?pgjrmۛجwluGtfh9uyP0D;Uڽ"OXlif$)&|ML0Zrm1[HXPlPR0'G=i2N+0e2]]9VTPO׮7h(F*癈'=QVZDF,d߬~TX G[`le69CR(!S2!P <0x<!1AQ "Raq02Br#SCTb ?Ζ"]mH5WR7k.ۛ!}Q~+yԏz|@T20S~Kek *zFf^2X*(@8r?CIuI|֓>^ExLgNUY+{.RѪ τV׸YTD I62'8Y27'\TP.6d&˦@Vqi|8-OΕ]ʔ U=TL8=;6c| !qfF3aů&~$l}'NWUs$Uk^SV:U# 6w++s&r+nڐ{@29 gL u"TÙM=6(^"7r}=6YݾlCuhquympǦ GjhsǜNlɻ}o7#S6aw4!OSrD57%|?x>L |/nD6?/8w#[)L7+6〼T ATg!%5MmZ/c-{1_Je"|^$'O&ޱմTrb$w)R$& N1EtdU3Uȉ1pM"N*(DNyd96.(jQ)X 5cQɎMyW?Q*!R>6=7)Xj5`J]e8%t!+'!1Q5 !1 AQaqё#2"0BRb?Gt^## .llQT $v,,m㵜5ubV =sY+@d{N! dnO<.-B;_wJt6;QJd.Qc%p{ 1,sNDdFHI0ГoXшe黅XۢF:)[FGXƹ/w_cMeD,ʡcc.WDtA$j@:) -# u c1<@ۗ9F)KJ-hpP]_x[qBlbpʖw q"LFGdƶ*s+ډ_Zc"?%t[IP 6J]#=ɺVvvCGsGh1 >)6|ey?Lӣm,4GWUi`]uJVoVDG< SB6ϏQ@ TiUlyOU0kfV~~}SZ@*WUUi##; s/[=!7}"WN]'(L! ~y5g9T̅JkbM' +s:S +B)v@Mj e Cf jE 0Y\QnzG1д~Wo{T9?`Rmyhsy3!HAD]mc1~2LSu7xT;j$`}4->L#vzŏILS ֭T{rjGKC;bpU=-`BsK.SFw4Mq]ZdHS0)tLg