# SPDX-License-Identifier: GPL-3.0-or-later # # This file is part of Nominatim. (https://nominatim.org) # # Copyright (C) 2024 by the Nominatim developer community. # For a full list of authors see the git log. """ Implementation of the 'convert' subcommand. """ from typing import Set, Any, Union, Optional, Sequence import argparse import asyncio from pathlib import Path from ..errors import UsageError from .args import NominatimArgs class WithAction(argparse.Action): """ Special action that saves a list of flags, given on the command-line as `--with-foo` or `--without-foo`. """ def __init__(self, option_strings: Sequence[str], dest: Any, default: bool = True, **kwargs: Any) -> None: if 'nargs' in kwargs: raise ValueError("nargs not allowed.") if option_strings is None: raise ValueError("Positional parameter not allowed.") self.dest_set = kwargs.pop('dest_set') full_option_strings = [] for opt in option_strings: if not opt.startswith('--'): raise ValueError("short-form options not allowed") if default: self.dest_set.add(opt[2:]) full_option_strings.append(f"--with-{opt[2:]}") full_option_strings.append(f"--without-{opt[2:]}") super().__init__(full_option_strings, argparse.SUPPRESS, nargs=0, **kwargs) def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: Union[str, Sequence[Any], None], option_string: Optional[str] = None) -> None: assert option_string if option_string.startswith('--with-'): self.dest_set.add(option_string[7:]) if option_string.startswith('--without-'): self.dest_set.discard(option_string[10:]) class ConvertDB: """ Convert an existing database into a different format. (EXPERIMENTAL) Dump a read-only version of the database in a different format. At the moment only a SQLite database suitable for reverse lookup can be created. """ def __init__(self) -> None: self.options: Set[str] = set() def add_args(self, parser: argparse.ArgumentParser) -> None: parser.add_argument('--format', default='sqlite', choices=('sqlite', ), help='Format of the output database (must be sqlite currently)') parser.add_argument('--output', '-o', required=True, type=Path, help='File to write the database to.') group = parser.add_argument_group('Switches to define database layout' '(currently no effect)') group.add_argument('--reverse', action=WithAction, dest_set=self.options, default=True, help='Enable/disable support for reverse and lookup API' ' (default: enabled)') group.add_argument('--search', action=WithAction, dest_set=self.options, default=True, help='Enable/disable support for search API (default: disabled)') group.add_argument('--details', action=WithAction, dest_set=self.options, default=True, help='Enable/disable support for details API (default: enabled)') def run(self, args: NominatimArgs) -> int: if args.output.exists(): raise UsageError(f"File '{args.output}' already exists. Refusing to overwrite.") if args.format == 'sqlite': from ..tools import convert_sqlite asyncio.run(convert_sqlite.convert(args.project_dir, args.output, self.options)) return 0 return 1