## @file
# Copyright (c) 2023, The OCE Build Authors. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
##
"""Methods for retrieving and handling config.plist files and patches."""
from functools import partial
from typing import Dict, List, Optional, Tuple, Union
from ocebuild.parsers.dict import merge_dict, nested_del, nested_get, nested_set
from ocebuild.parsers.plist import parse_plist
from ocebuild.parsers.schema import parse_schema
from ocebuild.parsers.yaml import parse_yaml
from ocebuild.sources import request
from ocebuild.sources.github import github_file_url
#NOTE: This import was remapped from 'third_party' to 'ocebuild.third_party'.
from ocebuild.third_party.cpython.pathlib import Path
[docs]def read_config(filepath: str,
                frontmatter: bool=False,
                flags: Optional[List[str]]=None
                ) -> Tuple[dict, Union[dict, None]]:
  """Reads a configuration file.
  Args:
    filepath: The path to the configuration file.
    frontmatter: Whether to include the file's frontmatter.
    flags: The flags to apply to the configuration file.
  Raises:
    ValueError: If the file extension is not supported.
  Returns:
    The configuration file.
    If `frontmatter` is `True`, a tuple containing:
      - The configuration file.
      - The frontmatter of the configuration file.
  """
  if not flags: flags = []
  with open(filepath, 'r', encoding='UTF-8') as f:
    file_ext = Path(filepath).suffix
    if   file_ext in ('.plist'):
      file = parse_plist(f)
      if frontmatter:
        return file, None
    elif file_ext in ('.yml', '.yaml'):
      if frontmatter:
        file, frontmatter = parse_yaml(f, flags=flags, frontmatter=True)
        return file, frontmatter
      else:
        file = parse_yaml(f, flags=flags)
    else:
      raise ValueError(f"Unsupported file extension: {file_ext}")
  return file 
[docs]def merge_configs(base: Union[str, Path],
                  *patches: Union[str, Path],
                  flags: Optional[List[str]]=None
                  ) -> Dict:
  """Merges a set of plist or yaml config files into a single config.
  Args:
    base: The base config file.
    *patches: The patch config files.
    flags: The flags to apply to the configuration file.
  Returns:
    The merged config.
  Raises:
    ValueError: If a patch file is not a plist or yaml file.
  Example:
    >>> merge_configs('base.plist', 'patch1.yml', 'patch2.plist', 'patch2.yaml')
    {...}
  """
  base_config, _ = read_config(base)
  if not flags: flags = []
  # Parse config patches
  for filepath in patches:
    patch, frontmatter = read_config(filepath, flags=flags, frontmatter=True)
    if isinstance(frontmatter, dict):
      flags += nested_get(frontmatter, ['flags'], default=[])
      if tags := nested_get(frontmatter, ['tags']):
        apply_preprocessor_tags(base_config, patch, tags)
      base_config = merge_dict(base_config, patch)
  return base_config 
[docs]def get_configuration_schema(repository: str='acidanthera/OpenCorePkg',
                             branch: str = 'master',
                             tag: Union[str, None] = None,
                             commit: Union[str, None] = None,
                             get_sample: bool=False,
                             **kwargs
                             ) -> Union[dict, Tuple[dict, dict]]:
  """Reads the Sample.plist schema from a OpenCorePkg version."""
  # Resolve file urls for the given repository parameters.
  file_url = partial(github_file_url,
                     repository=repository,
                     branch=branch,
                     tag=tag,
                     commit=commit,
                     raw=True)
  # Get the reference configuration and sample plist urls
  configuration_url = file_url(path='Docs/Configuration.tex')
  sample_plist_url = file_url(path='Docs/Sample.plist')
  sample_plist = parse_plist(request(url=sample_plist_url).text())
  with request(url=configuration_url).text() as file:
    schema = parse_schema(file, sample_plist, **kwargs)
  if get_sample: return schema, sample_plist
  return schema 
#TODO: Sort kexts by dependency order, including support for:
# - Including existing entry properties (pointing to an existing `BundlePath`)
# - Disable entries on:
#   - Duplicate CFBundleIdentifiers with overlapping Max/MinKernel ranges
#   - Missing CFBundleIdentifiers (i.e. unresolved dependencies)
#   - Missing CFBundleIdentifiers/CFBundleExecutable fields in Info.plist
__all__ = [
  # Functions (4)
  "read_config",
  "apply_preprocessor_tags",
  "merge_configs",
  "get_configuration_schema"
]