#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (C) 2022 Kunal Mehta import json import requests import subprocess from copy import deepcopy from pathlib import Path TOOLFORGE_USERINDEX = [ "archive", "filearchive", "logging", "oldimage", "recentchanges", "revision", ] TOOLFORGE_ACTOR = [ "user", "archive", "ipblocks", "image", "oldimage", "filearchive", "recentchanges", "logging", "revision" ] def snake_to_camel(phrase: str) -> str: """snake_case to CamelCase""" sp = phrase.split('_') return ''.join(word.title() for word in sp) def strip_prefix(items: list) -> dict: """Strip a common prefix from a list of items""" data = {} for item in items: data[item] = item # Unmodified copy of the "last" round last = deepcopy(data) while True: # First segment of the first thing first = list(data.values())[0].split('_', 1)[0] + '_' print(first) for key in data: print(key) print(data[key]) if data[key].startswith(first): # It also starts with it, shift over by the segment data[key] = data[key][len(first):] else: return last # Finished a round last = deepcopy(data) def build_rust(table: dict, toolforge=False) -> str: if toolforge: rust = '#[cfg(feature = "toolforge")]\n' rust += '#[cfg_attr(docs, doc(cfg(feature = "toolforge")))]\n' else: rust = "" name = table["name"] rust += f"/// `{name}` table\n" rust += "///\n" if toolforge: rust += "/// This is an optimized view for Toolforge users.\n" else: rust += f"/// Documentation may be available on [mediawiki.org](https://www.mediawiki.org/wiki/Manual:{name}_table).\n" rust += "#[non_exhaustive]\n" rust += "#[derive(Copy, Clone, IdenStatic)]\n" rust += f'#[iden="{name}"]\n' camel_name = snake_to_camel(name) print(f"Generating {camel_name} for {name}") rust += f"pub enum {camel_name} {{\nTable,\n" columns = [item["name"] for item in table["columns"]] stripped = strip_prefix(columns) for column, name in sorted(stripped.items()): camel_name = snake_to_camel(name) rust += f"/// `{column}` column\n" rust += f'#[iden="{column}"]\n' rust += f"{camel_name},\n" rust += "}" rust += "\n\n" return rust def make(repo: str, path: str, filename: str): req = requests.get(f"https://github.com/wikimedia/{repo.replace('/', '-')}/raw/master/{path}") req.raise_for_status() tables = req.json() rust = "// This file is autogenerated.\n" rust += f"//! Schema definitions for the `{repo}` repository\n" rust += "use sea_query::IdenStatic;\n\n" for table in sorted(tables, key=lambda x: x["name"]): rust += build_rust(table) if table["name"] in TOOLFORGE_USERINDEX: table["name"] = f"{table['name']}_userindex" rust += build_rust(table, toolforge=True) if table["name"] == "actor": for variant in TOOLFORGE_ACTOR: table["name"] = f"actor_{variant}" rust += build_rust(table, toolforge=True) Path(f"src/{filename}.rs").write_text(rust) subprocess.check_call(["cargo", "fmt"]) print("Done!") def main(): make('mediawiki/core', 'maintenance/tables.json', 'core') make('mediawiki/extensions/Linter', 'sql/tables.json', 'linter') make('mediawiki/extensions/PageAssessments', 'db/tables.json', 'page_assessments') make('mediawiki/extensions/ProofreadPage', 'sql/tables.json', 'proofread_page') if __name__ == "__main__": main()