1# Copyright 2020 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Defines arguments for the pw command."""
15
16import argparse
17import logging
18from pathlib import Path
19import sys
20from typing import NoReturn
21
22from pw_cli import plugins
23from pw_cli.branding import banner
24
25_HELP_HEADER = '''The Pigweed command line interface (CLI).
26
27Example uses:
28    pw logdemo
29    pw --loglevel debug watch out/clang
30'''
31
32
33def parse_args() -> argparse.Namespace:
34    return _parser().parse_args()
35
36
37def print_banner() -> None:
38    """Prints the PIGWEED (or project specific) banner to stderr."""
39    print(banner(), file=sys.stderr)
40
41
42def format_help(registry: plugins.Registry) -> str:
43    """Returns the pw help information as a string."""
44    return f'{_parser().format_help()}\n{registry.short_help()}'
45
46
47class _ArgumentParserWithBanner(argparse.ArgumentParser):
48    """Parser that the Pigweed banner when there are parsing errors."""
49    def error(self, message: str) -> NoReturn:
50        print_banner()
51        self.print_usage(sys.stderr)
52        self.exit(2, f'{self.prog}: error: {message}\n')
53
54
55def _parser() -> argparse.ArgumentParser:
56    """Creates an argument parser for the pw command."""
57    argparser = _ArgumentParserWithBanner(
58        prog='pw',
59        add_help=False,
60        description=_HELP_HEADER,
61        formatter_class=argparse.RawDescriptionHelpFormatter)
62
63    def directory(arg: str) -> Path:
64        path = Path(arg)
65        if path.is_dir():
66            return path.resolve()
67
68        raise argparse.ArgumentTypeError(f'{path} is not a directory')
69
70    def log_level(arg: str) -> int:
71        try:
72            return getattr(logging, arg.upper())
73        except AttributeError:
74            raise argparse.ArgumentTypeError(
75                f'{arg.upper()} is not a valid log level')
76
77    # Do not use the built-in help argument so that displaying the help info can
78    # be deferred until the pw plugins have been registered.
79    argparser.add_argument('-h',
80                           '--help',
81                           action='store_true',
82                           help='Display this help message and exit')
83    argparser.add_argument(
84        '-C',
85        '--directory',
86        type=directory,
87        default=Path.cwd(),
88        help='Change to this directory before doing anything')
89    argparser.add_argument(
90        '-l',
91        '--loglevel',
92        type=log_level,
93        default=logging.INFO,
94        help='Set the log level (debug, info, warning, error, critical)')
95    argparser.add_argument('--no-banner',
96                           action='store_true',
97                           help='Do not print the Pigweed banner')
98    argparser.add_argument(
99        'command',
100        nargs='?',
101        help='Which command to run; see supported commands below')
102    argparser.add_argument(
103        'plugin_args',
104        metavar='...',
105        nargs=argparse.REMAINDER,
106        help='Remaining arguments are forwarded to the command')
107
108    return argparser
109