#!/usr/bin/env python3

from pathlib import Path
import argparse
import subprocess
import sys


NOTHING_TO_BE_DONE = 0
NEED_UPDATE = 1
FAILURE = 2

DEFAULT_GIR_FILES_DIRECTORY = Path('./gir-files')
DEFAULT_GIR_DIRECTORY = Path('./gir/')
DEFAULT_GIR_PATH = DEFAULT_GIR_DIRECTORY / 'target/release/gir'


def run_command(command, folder=None):
    if folder is None:
        folder = "."
    ret = subprocess.run(command, cwd=folder)
    if ret.returncode != 0:
        print("Command `{}` failed with `{}`...".format(command, ret))
        return False
    return True


def update_workspace():
    return run_command(['cargo', 'build', '--release'], 'gir')


def ask_yes_no_question(question, conf):
    question = '{} [y/N] '.format(question)
    if conf.yes:
        print(question + 'y')
        return True
    if sys.version_info[0] < 3:
        line = raw_input(question)
    else:
        line = input(question)
    return line.strip().lower() == 'y'


def def_check_submodule(submodule_path, conf):
    if any(submodule_path.iterdir()):
        return NOTHING_TO_BE_DONE
    print('=> Initializing {} submodule...'.format(submodule_path))
    if not run_command(['git', 'submodule', 'update', '--init', submodule_path]):
        return FAILURE
    print('<= Done!')

    if ask_yes_no_question('Do you want to update {} submodule?'.format(submodule_path), conf):
        print('=> Updating submodule...')
        if not run_command(['git', 'reset', '--hard', 'HEAD'], submodule_path):
            return FAILURE
        if not run_command(['git', 'pull', '-f', 'origin', 'master'], submodule_path):
            return FAILURE
        print('<= Done!')
        return NEED_UPDATE
    return NOTHING_TO_BE_DONE


def build_gir_if_needed(updated_submodule):
    if updated_submodule == FAILURE:
        return False
    print('=> Building gir...')
    if update_workspace():
        print('<= Done!')
    else:
        print('<= Failed...')
        return False
    return True


def regen_crates(path, conf, level=0):
    if path.is_dir():
        for entry in path.iterdir():
            if level < 3 and not regen_crates(entry, conf, level + 1):
                return False
    elif path.name.startswith("Gir") and path.suffix == ".toml":
        print('==> Regenerating "{}"...'.format(path))

        args = [conf.gir_path, '-c', path, '-o', path.parent, '-d', conf.gir_files_path]
        if path.parent.name.endswith("sys"):
            args.extend(['-m', 'sys'])
        error = False
        try:
            error = not run_command(args)
        except Exception as err:
            print('The following error occurred: {}'.format(err))
            error = True
        if error:
            if not ask_yes_no_question('Do you want to continue?', conf):
                return False
        print('<== Done!')
    return True


def valid_path(path):
    path = Path(path)
    if not path.exists():
        raise argparse.ArgumentTypeError("`{}` no such file or directory".format(path))
    return path


def directory_path(path):
    path = Path(path)
    if not path.is_dir():
        raise argparse.ArgumentTypeError("`{}` directory not found".format(path))
    return path


def file_path(path):
    path = Path(path)
    if not path.is_file():
        raise argparse.ArgumentTypeError("`{}` file not found".format(path))
    return path


def parse_args():
    parser = argparse.ArgumentParser(description='Helper to regenerate gtk-rs crates using gir.',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('path', nargs="*", default=[Path('.')],
                        type=valid_path,
                        help='Paths in which to look for Gir.toml files')
    parser.add_argument('--gir-files', dest="gir_files_path", default=DEFAULT_GIR_FILES_DIRECTORY,
                        type=directory_path,
                        help='Path of the gir-files folder')
    parser.add_argument('--gir-path', default=DEFAULT_GIR_PATH,
                        type=file_path,
                        help='Path of the gir executable to run')
    parser.add_argument('--yes', action='store_true',
                        help=' Always answer `yes` to any question asked by the script')
    parser.add_argument('--no-fmt', action='store_true',
                        help='If set, this script will not run `cargo fmt`')

    return parser.parse_args()


def main():
    conf = parse_args()

    if conf.gir_files_path == DEFAULT_GIR_FILES_DIRECTORY:
        if def_check_submodule(conf.gir_files_path, conf) == FAILURE:
            return 1

    if conf.gir_path == DEFAULT_GIR_PATH:
        if not build_gir_if_needed(def_check_submodule(DEFAULT_GIR_DIRECTORY, conf)):
            return 1

    print('=> Regenerating crates...')
    for path in conf.path:
        print('=> Looking in path `{}`'.format(path))
        if not regen_crates(path, conf):
            return 1
    if not conf.no_fmt and not run_command(['cargo', 'fmt']):
        return 1
    print('<= Done!')
    print("Don't forget to check if everything has been correctly generated!")
    return 0


if __name__ == "__main__":
    sys.exit(main())

@@ -2009,17 +2009,8 @@
 conf
-, level=0
 ):%0A 
@@ -2056,24 +2056,33 @@
 in path.
-iterdir(
+rglob(%22Gir*.toml%22
 ):%0A     
@@ -2095,22 +2095,8 @@
  if 
-level %3C 3 and 
 not 
@@ -2123,19 +2123,8 @@
 conf
-, level + 1
 ):%0A 
@@ -2169,57 +2169,26 @@
 ath.
-name.startswith(%22Gir%22) and path.suffix == %22
+match(%22Gir*
 .toml%22
+)
 :%0A  
@@ -2759,24 +2759,122 @@
 %3C== Done!')%0A
+    else:%0A        print('==%3E %7B%7D is not a valid Gir*.toml file'.format(path))%0A        return False%0A
     return T

Use pathlibs rglob to find all Gir*.toml