diff --git a/jc/lib.py b/jc/lib.py index 44c58592..a195855d 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -108,6 +108,7 @@ 'openvpn', 'os-prober', 'os-release', + 'pacman', 'passwd', 'path', 'path-list', diff --git a/jc/parsers/pacman.py b/jc/parsers/pacman.py new file mode 100644 index 00000000..12e81105 --- /dev/null +++ b/jc/parsers/pacman.py @@ -0,0 +1,196 @@ +r"""jc - JSON Convert `pacman` command output parser + +Supports the following `pacman` arguments: + +- `-Si` +- `-Sii` +- `-Qi` +- `-Qii` + +Usage (cli): + + $ pacman -Si | jc --pacman + +or + + $ jc pacman -Si + +Usage (module): + + import jc + result = jc.parse('pacman', pacman_command_output) + +Schema: + + [ + { + "repository": string, + "name": string, + "version": string, + "description": string, + "architecture": string, + "url": string, + "licenses": [ + string + ], + "groups": [ + string + ], + "provides": [ + string + ], + "depends_on": [ + string + ], + "optional_deps": [ + { + "name": string, + "description": string + } + ], + "conflicts_with": [ + string + ], + "replaces": [ + string + ], + "download_size": string, + "installed_size": string, + "packager": string, + "build_date": string, + "validated_by": [ + string + ], + "backup_files": [ + string + ] + } + ] + +Examples: + + $ pacman | jc --pacman -p + [] + + $ pacman | jc --pacman -p -r + [] +""" +from typing import List, Dict +from jc.jc_types import JSONDictType +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`pacman` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['command', 'file'] + magic_commands = ['pacman'] + + +__version__ = info.version + + +def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured to conform to the schema. + """ + list_list = [ + 'licenses', 'groups', 'provides', 'depends_on', 'optional_deps', + 'conflicts_with', 'replaces', 'validated_by', 'backup_files' + ] + + + return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> List[JSONDictType]: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: List[Dict] = [] + entry_obj: Dict = {} + multiline_fields = {'required_by', 'optional_deps', 'backup_files'} + multiline_list: List = [] + multiline_key = '' + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()): + splitline = line.split(' : ', maxsplit=1) + + if len(splitline) == 2: + # this is a key/value pair + key, val = splitline + key = key.strip() + key = jc.utils.normalize_key(key) + val = val.strip() + + # new entries can start with "Repository" or "Name" + if (key == 'name' or key == 'repository') and len(entry_obj) > 2: + if multiline_list: + entry_obj[multiline_key] = multiline_list + multiline_list = [] + multiline_key = '' + if entry_obj: + raw_output.append(entry_obj) + entry_obj = {} + entry_obj[key] = val + continue + + if key in multiline_fields: + multiline_list = [] + if val != 'None': + multiline_list.append(val) + multiline_key = key + continue + + if key not in multiline_fields: + if multiline_list: + entry_obj[multiline_key] = multiline_list + multiline_list = [] + multiline_key = '' + entry_obj[key] = val if val != 'None' else None + continue + + # multiline field continuation lines + multiline_list.append(line.strip()) + continue + + # grab the last entry + if entry_obj: + if multiline_list: + entry_obj[multiline_key] = multiline_list + multiline_list = [] + multiline_key = '' + raw_output.append(entry_obj) + + return raw_output if raw else _process(raw_output)