ex-cli 1.20.0

Command line tool to find, filter, sort and list files.
Documentation
#!/usr/bin/env python3
import argparse
import os
import re
import sys
from argparse import Namespace
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Any, Optional, TextIO, Union

type AnyIO = Union[TextIO, Any]

class CargoModifier:

    def __init__(self):
        self.original = None
        self.version = None
        self.dirty = False

    @staticmethod
    def query_file(path: Path):
        with open(path, 'r') as reader:
            for line in reader:
                if match := re.search(r'^version = "(.+)"$', line):
                    version = match.group(1)
                    print(f'{path}: {version}')

    # noinspection PyProtectedMember
    def modify_file(self, path: Path, version: Optional[str]):
        with open(path, 'r') as reader:
            with NamedTemporaryFile('w', dir=path.parent) as writer:
                self.modify_stream(reader, writer, version)
                if self.dirty:
                    writer._closer.delete = False
        if self.dirty:
            if self.original and self.version:
                print(f'{path}: {self.original} => {self.version}')
            else:
                print(path)
            os.rename(writer.name, path)

    def modify_stream(self, reader: TextIO, writer: AnyIO, version: Optional[str]):
        for line in reader:
            if match := re.search(r'^version = "(.+)"$', line):
                self.original = match.group(1)
                self.version = version or self.modify_version(self.original)
                if self.version != self.original:
                    line = f'version = "{self.version}"\n'
                    self.dirty = True
            print(line, end='', file=writer)

    @staticmethod
    def modify_version(version: str) -> str:
        suffix = ''
        if match := re.search(r'^(.+)(-\w+)$', version):
            version, suffix = match.groups()
        # 1.9.0-SNAPSHOT => 1.10.0-SNAPSHOT
        # 1.9.1-SNAPSHOT => 1.9.2-SNAPSHOT
        # 1.9.2-SNAPSHOT => 1.9.3-SNAPSHOT
        items = [int(x) for x in version.split('.')]
        if len(items) == 3:
            if items[2] == 0:
                items[1] += 1
            else:
                items[2] += 1
            version = '.'.join(str(x) for x in items)
        return f'{version}{suffix}'

    def read_stream(self, reader: TextIO):
        for line in reader:
            if match := re.search(r'^version = "(.+)"$', line):
                self.version = match.group(1)

def parse_args() -> Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument('version', nargs='?', help='set version number')
    parser.add_argument('-b', '--bump', action='store_true', help='bump version number')
    # parser.add_argument('-c', '--commit', action='store_true', help='create Git commit')
    return parser.parse_args()

def run_main():
    path = Path(__file__).parent.with_name('Cargo.toml')
    settings = parse_args()
    modifier = CargoModifier()
    if settings.version or settings.bump:
        modifier.modify_file(path, settings.version)
    else:
        modifier.query_file(path)

try:
    run_main()
except (OSError, KeyboardInterrupt) as error:
    print(error, file=sys.stderr)