# SPDX-License-Identifier: Apache-2.0
# Copyright © 2022-2024 Intel Corporation

"""Type definitions for cargo manifest files."""

from __future__ import annotations
import typing as T

from typing_extensions import Literal, TypedDict, Required

EDITION = Literal['2015', '2018', '2021']
CRATE_TYPE = Literal['bin', 'lib', 'dylib', 'staticlib', 'cdylib', 'rlib', 'proc-macro']
LINT_LEVEL = Literal['allow', 'deny', 'forbid', 'warn']


class FromWorkspace(TypedDict):

    """An entry or section that is copied from the workspace."""

    workspace: bool


Package = TypedDict(
    'Package',
    {
        'name': Required[str],
        'version': Required[T.Union[FromWorkspace, str]],
        'authors': T.Union[FromWorkspace, T.List[str]],
        'edition': T.Union[FromWorkspace, EDITION],
        'rust-version': T.Union[FromWorkspace, str],
        'description': T.Union[FromWorkspace, str],
        'readme': T.Union[FromWorkspace, str],
        'license': T.Union[FromWorkspace, str],
        'license-file': T.Union[FromWorkspace, str],
        'keywords': T.Union[FromWorkspace, T.List[str]],
        'categories': T.Union[FromWorkspace, T.List[str]],
        'homepage': T.Union[FromWorkspace, str],
        'repository': T.Union[FromWorkspace, str],
        'documentation': T.Union[FromWorkspace, str],
        'workspace': str,
        'build': str,
        'links': str,
        'include': T.Union[FromWorkspace, T.List[str]],
        'exclude': T.Union[FromWorkspace, T.List[str]],
        'publish': T.Union[FromWorkspace, bool],
        'metadata': T.Dict[str, T.Dict[str, str]],
        'default-run': str,
        'autolib': bool,
        'autobins': bool,
        'autoexamples': bool,
        'autotests': bool,
        'autobenches': bool,
    },
    total=False,
)
"""A description of the Package Dictionary."""

class Badge(TypedDict):

    """An entry in the badge section."""

    status: Literal['actively-developed', 'passively-developed', 'as-is', 'experimental', 'deprecated', 'none']
    repository: str


Dependency = TypedDict(
    'Dependency',
    {
        'version': str,
        'registry': str,
        'git': str,
        'branch': str,
        'rev': str,
        'path': str,
        'optional': bool,
        'package': str,
        'default-features': bool,
        'features': T.List[str],
    },
    total=False,
)
"""An entry in the *dependencies sections."""


DependencyV = T.Union[Dependency, str]
"""A Dependency entry, either a string or a Dependency Dict."""


_BaseBuildTarget = TypedDict(
    '_BaseBuildTarget',
    {
        'path': str,
        'test': bool,
        'doctest': bool,
        'bench': bool,
        'doc': bool,
        'plugin': bool,
        'proc-macro': bool,
        'harness': bool,
        'edition': EDITION,
        'crate-type': T.List[CRATE_TYPE],
        'required-features': T.List[str],
    },
    total=False,
)


class BuildTarget(_BaseBuildTarget, total=False):

    name: Required[str]


class LibTarget(_BaseBuildTarget, total=False):

    name: str


class Target(TypedDict):

    """Target entry in the Manifest File."""

    dependencies: T.Dict[str, T.Union[FromWorkspace, DependencyV]]


Lint = TypedDict(
    'Lint',
    {
        'level': Required[LINT_LEVEL],
        'priority': int,
        'check-cfg': T.List[str],
    },
    total=True,
)
"""The representation of a linter setting.

This does not include the name or tool, since those are the keys of the
dictionaries that point to Lint.
"""


LintV = T.Union[Lint, str]
"""A Lint entry, either a string or a Lint Dict."""


class Workspace(TypedDict):

    """The representation of a workspace.

    In a vritual manifest the :attribute:`members` is always present, but in a
    project manifest, an empty workspace may be provided, in which case the
    workspace is implicitly filled in by values from the path based dependencies.

    the :attribute:`exclude` is always optional
    """

    members: T.List[str]
    exclude: T.List[str]
    package: Package
    dependencies: T.Dict[str, DependencyV]


Manifest = TypedDict(
    'Manifest',
    {
        'package': Package,
        'badges': T.Dict[str, Badge],
        'dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
        'dev-dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
        'build-dependencies': T.Dict[str, T.Union[FromWorkspace, DependencyV]],
        'lib': LibTarget,
        'bin': T.List[BuildTarget],
        'test': T.List[BuildTarget],
        'bench': T.List[BuildTarget],
        'example': T.List[BuildTarget],
        'features': T.Dict[str, T.List[str]],
        'target': T.Dict[str, Target],
        'workspace': Workspace,
        'lints': T.Union[FromWorkspace, T.Dict[str, T.Dict[str, LintV]]],

        # TODO: patch?
        # TODO: replace?
    },
    total=False,
)
"""The Cargo Manifest format."""


class CargoLockPackage(TypedDict, total=False):

    """A description of a package in the Cargo.lock file format."""

    name: str
    version: str
    source: str
    checksum: str


class CargoLock(TypedDict, total=False):

    """A description of the Cargo.lock file format."""

    version: int
    package: T.List[CargoLockPackage]
    metadata: T.Dict[str, str]
